Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE

This commit is contained in:
Razvan Maries 2018-11-27 19:38:10 +02:00
Родитель da6839f947 99a5f987b3
Коммит a6f88b57a1
425 изменённых файлов: 11511 добавлений и 4038 удалений

Просмотреть файл

@ -40,6 +40,13 @@ widget/tests/.*
xpcom/glue/tests/.*
xpcom/tests/.*
# Generated by protobuf
.*/.*.pb.h
.*/.*.pb.cc
# Autogenerated file
media/mp4parse-rust/mp4parse.h
# Generated from ./tools/rewriting/ThirdPartyPaths.txt
# awk '{print ""$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
browser/components/translation/cld2/.*
@ -97,7 +104,7 @@ js/src/vtune/legacy/.*
media/ffvpx/.*
media/gmp-clearkey/0.1/openaes/.*
media/kiss_fft/.*
media/libav/.*
media/libaom/.*
media/libcubeb/.*
media/libjpeg/.*
media/libmkv/.*
@ -115,6 +122,7 @@ media/libwebp/.*
media/libyuv/.*
media/mtransport/third_party/.*
media/openmax_dl/.*
media/openmax_il/.*
media/webrtc/signaling/src/sdp/sipcc/.*
media/webrtc/trunk/.*
mfbt/decimal/.*
@ -156,6 +164,7 @@ toolkit/components/url-classifier/protobuf/.*
toolkit/crashreporter/google-breakpad/.*
toolkit/recordreplay/udis86/.*
tools/fuzzing/libfuzzer/.*
tools/profiler/core/vtune/.*
xpcom/build/mach_override.c
xpcom/build/mach_override.h
xpcom/io/crc32c.c

Просмотреть файл

@ -9,6 +9,7 @@
#include <type_traits>
// clang-format off
/******************************************************************************
The following accessible states aren't translated, just ignored:
STATE_READONLY: Supported indirectly via EXT_STATE_EDITABLE
@ -36,6 +37,7 @@ The following ATK states are not supported:
ATK_STATE_TRUNCATED: No clear use case. Indicates that an object's onscreen content is truncated,
e.g. a text value in a spreadsheet cell. No IA2 state.
******************************************************************************/
// clang-format on
enum EStateMapEntryType {
kMapDirectly,

Просмотреть файл

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// clang-format off
/**
* Usage: declare the macro ROLE()with the following arguments:
* ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule)
@ -1629,3 +1630,4 @@ ROLE(FORM_LANDMARK,
IA2_ROLE_FORM,
java::SessionAccessibility::CLASSNAME_VIEW,
eNoNameRule)
// clang-format on

Просмотреть файл

@ -1518,7 +1518,9 @@ HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
LayoutDeviceIntRect caretRect = LayoutDeviceIntRect::FromUnknownRect(
rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()));
// clang-format off
// ((content screen origin) - (content offset in the widget)) = widget origin on the screen
// clang-format on
caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
// Correct for character size, so that caret always matches the size of

Просмотреть файл

@ -1,4 +1,6 @@
/* clang-format off */
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* clang-format on */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -651,12 +651,14 @@ AccessibleWrap::get_accFocus(
VariantInit(pvarChild);
// clang-format off
// VT_EMPTY: None. This object does not have the keyboard focus itself
// and does not contain a child that has the keyboard focus.
// VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus.
// VT_I4: lVal contains the child ID of the child element with the keyboard focus.
// VT_DISPATCH: pdispVal member is the address of the IDispatch interface
// for the child object with the keyboard focus.
// clang-format on
if (IsDefunct())
return CO_E_OBJNOTCONNECTED;

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++ final; tab-width: 2 final; indent-tabs-mode: nil final; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,

Просмотреть файл

@ -1,6 +1,8 @@
module.exports = {
// When adding items to this file please check for effects on sub-directories.
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"ecmaFeatures": {
"jsx": true
},
@ -15,6 +17,11 @@ module.exports = {
"promise", // require("eslint-plugin-promise")
"react" // require("eslint-plugin-react")
],
"settings": {
"react": {
"version": "16.2.0"
}
},
"extends": [
"eslint:recommended",
"plugin:mozilla/recommended" // require("eslint-plugin-mozilla")

Просмотреть файл

@ -30,23 +30,4 @@ Name | Used for | Type | Example value
}
```
## Admin Interface
* Navigate to `about:newtab#asrouter`
* See all available messages and message providers
* Block, unblock or force show a specific message
## Snippet Preview
* Whitelist the provider host that will serve the messages
* In `about:config`, `browser.newtab.activity-stream.asrouter.whitelistHosts` can contain a array of hosts
* Example value `["gist.github.com", "gist.githubusercontent.com", "localhost:8000"]`
* Errors are surfaced in the `Console` tab of the `Browser Toolbox` ([read how to enable](https://developer.mozilla.org/en-US/docs/Tools/Browser_Toolbox))
* Navigate to `about:newtab?endpoint=<URL>`
* Example `https://gist.githubusercontent.com/piatra/70234f08696c0a0509d7ba5568cd830f/raw/68370f34abc134142c64b6f0a9b9258a06de7aa3/messages.json`
* URL should be from an endpoint that was just whitelisted
* The snippet preview should imediately load
* The endpoint must be HTTPS, the host must be whitelisted
* Errors are surfaced in the `Console` tab of the `Browser Toolbox`
### [Snippet message format documentation](https://github.com/mozilla/activity-stream/blob/master/content-src/asrouter/schemas/message-format.md)

Просмотреть файл

@ -0,0 +1,76 @@
# Using ASRouter Devtools
## How to enable ASRouter devtools
- In `about:config`, set `browser.newtabpage.activity-stream.asrouter.devtoolsEnabled` to `true`
- Visit `about:newtab#asrouter` to see the devtools.
## Overview of ASRouter devtools
![Devtools image](./debugging-guide.png)
## How to enable/disable a provider
To enable a provider such as `snippets`, Look at the list of "Message Providers" at the top of the page. Make sure the checkbox is checked next to the provider you want to enable.
To disable it, uncheck the checkbox. You should see a red label indicating the provider is now disabled.
## How to see all messages from a provider
(Only available in Firefox 65+)
In order to see all active messages for a current provider such as `snippets`, use the drop down selector under the "Messages" section. Select the name of the provider you are interested in.
The messages on the page should now be filtered to include only the provider you selected.
## How to test telemetry pings
To test telemetry pings, complete the the following steps:
- In about:config, set:
- `browser.newtabpage.activity-stream.telemetry` to `true`
- `browser.ping-centre.log` to `true`
- Open the Browser Toolbox devtools (Tools > Developer > Browser Toolbox) and switch to the console tab. Add a filter for for `activity-stream-router` to only display relevant pings:
![Devtools telemetry pong](./telemetry-screenshot.png)
You should now see pings show up as you view/interact with ASR messages/templates.
## Snippets debugging
### How to test legacy snippets
To make sure the legacy snippets system is still running properly, do the following:
- Load test data for legacy snippets. In about:config, set `browser.aboutHomeSnippets.updateUrl` to `https://snippets.allizom.org/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/`
- Disable ASR snippets
- set up your ASR devtools (see steps above)
- visit `about:newtab#asrouter`
- Uncheck the checkbox next to snippets
- Check `about:newtab` to make sure legacy snippets are loading
- visit `about:newtab`
- Type gSnippetsMap.clear() into the devtools console and refresh the page
- Open the devtools again
- Ensure you see the message “Legacy snippets: Successfully added snippets.”
- Ensure you visually see snippets on the page
### How to view preview URLs
Follow these steps to view preview URLs (e.g. `about:newtab?endpoint=https://gist.githubusercontent.com/piatra/d193ca7e0f513cc19fc6a1d396c214f7/raw/8bcaf9548212e4c613577e839198cc14e7317630/newsletter_snippet.json`)
#### IMPORTANT NOTES
- Links to URLs starting with `about:newtab` cannot be clicked on directly. They must be copy and pasted into the address bar.
- Previews should only be tested in `Firefox 64` and later.
- The endpoint must be HTTPS, the host must be whitelisted (see testing instructions below)
- Errors are surfaced in the `Console` tab of the `Browser Toolbox`
#### Testing instructions
- If your endpoint URL has a host name of `snippets-admin.mozilla.org`, you can paste the URL into the address bar view it without any further steps.
- If your endpoint URL starts with some other host name, it must be **whitelisted**. Open the Browser Toolbox devtools (Tools > Developer > Browser Toolbox) and paste the following code (where `gist.githubusercontent.com` is the hostname of your endpoint URL):
```js
Services.prefs.setStringPref(
"browser.newtab.activity-stream.asrouter.whitelistHosts",
"[\"gist.githubusercontent.com\"]"
);
```
- Restart the browser
- You should now be able to paste the URL into the address bar and view it.

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 242 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 102 KiB

Просмотреть файл

@ -264,7 +264,7 @@ export class ASRouterAdmin extends React.PureComponent {
let label = "local";
if (provider.type === "remote") {
label = (<span>endpoint (<a className="providerUrl" target="_blank" href={info.url}>{info.url}</a>)</span>);
label = (<span>endpoint (<a className="providerUrl" target="_blank" href={info.url} rel="noopener noreferrer">{info.url}</a>)</span>);
} else if (provider.type === "remote-settings") {
label = `remote settings (${provider.bucket})`;
}
@ -355,6 +355,13 @@ export class ASRouterAdmin extends React.PureComponent {
render() {
return (<div className="asrouter-admin outer-wrapper">
<h1>AS Router Admin</h1>
<p className="helpLink">
<span className="icon icon-small-spacer icon-info" />
{" "}
<span>
Need help using these tools? Check out our <a target="blank" href="https://github.com/mozilla/activity-stream/blob/master/content-src/asrouter/docs/debugging-docs.md">documentation</a>
</span>
</p>
<h2>Targeting Utilities</h2>
<button className="button" onClick={this.expireCache}>Expire Cache</button> (This expires the cache in ASR Targeting for bookmarks and top sites)
<h2>Message Providers <button title="Restore all provider settings that ship with Firefox" className="button" onClick={this.resetPref}>Restore default prefs</button></h2>

Просмотреть файл

@ -108,4 +108,15 @@
.errorState {
border: 1px solid $red-60;
}
.helpLink {
padding: 10px;
display: flex;
background: $yellow-50;
border-radius: 3px;
a {
text-decoration: underline;
}
}
}

Просмотреть файл

@ -1595,6 +1595,13 @@ main {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace; }
.asrouter-admin .errorState {
border: 1px solid #D70022; }
.asrouter-admin .helpLink {
padding: 10px;
display: flex;
background: #FFE900;
border-radius: 3px; }
.asrouter-admin .helpLink a {
text-decoration: underline; }
.pocket-logged-in-cta {
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1598,6 +1598,13 @@ main {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace; }
.asrouter-admin .errorState {
border: 1px solid #D70022; }
.asrouter-admin .helpLink {
padding: 10px;
display: flex;
background: #FFE900;
border-radius: 3px; }
.asrouter-admin .helpLink a {
text-decoration: underline; }
.pocket-logged-in-cta {
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1595,6 +1595,13 @@ main {
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace; }
.asrouter-admin .errorState {
border: 1px solid #D70022; }
.asrouter-admin .helpLink {
padding: 10px;
display: flex;
background: #FFE900;
border-radius: 3px; }
.asrouter-admin .helpLink a {
text-decoration: underline; }
.pocket-logged-in-cta {
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -2565,7 +2565,7 @@ class ASRouterAdmin extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureCom
"endpoint (",
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(
"a",
{ className: "providerUrl", target: "_blank", href: info.url },
{ className: "providerUrl", target: "_blank", href: info.url, rel: "noopener noreferrer" },
info.url
),
")"
@ -2767,6 +2767,22 @@ class ASRouterAdmin extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureCom
null,
"AS Router Admin"
),
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(
"p",
{ className: "helpLink" },
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement("span", { className: "icon icon-small-spacer icon-info" }),
" ",
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(
"span",
null,
"Need help using these tools? Check out our ",
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(
"a",
{ target: "blank", href: "https://github.com/mozilla/activity-stream/blob/master/content-src/asrouter/docs/debugging-docs.md" },
"documentation"
)
)
),
react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(
"h2",
null,

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -145,6 +145,7 @@ pocket_read_more=المواضيع الشائعة:
pocket_read_even_more=اعرض المزيد من الأخبار
pocket_more_reccommendations=مقترحات أخرى
pocket_learn_more=اطّلع على المزيد
pocket_how_it_works=آلية العمل
pocket_cta_button=نزِّل بوكِت
pocket_cta_text=احفظ القصص التي تحبّها في بوكِت، وزوّد عقلك بمقالات رائعة.

Просмотреть файл

@ -143,9 +143,9 @@ pocket_read_more=Beliebte Themen:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Weitere Nachrichten ansehen
pocket_more_reccommendations=Mehr Empfehlungen
pocket_learn_more=Weitere Informationen
pocket_how_it_works=Wie es funktioniert
pocket_cta_button=Pocket holen
pocket_cta_text=Speichern Sie Ihre Lieblingstexte in Pocket und gewinnen Sie gedankenreiche Einblicke durch faszinierende Texte.
@ -196,7 +196,6 @@ firstrun_form_header=E-Mail-Adresse eingeben
firstrun_form_sub_header=um sich bei Firefox Sync anzumelden.
firstrun_email_input_placeholder=E-Mail
firstrun_invalid_input=Gültige E-Mail-Adresse erforderlich
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
@ -207,3 +206,6 @@ firstrun_privacy_notice=Datenschutzhinweis
firstrun_continue_to_login=Weiter
firstrun_skip_login=Diesen Schritt überspringen
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Menü öffnen

Просмотреть файл

@ -144,6 +144,7 @@ pocket_read_more=Temas populares:
# end of the list of popular topic links.
pocket_read_even_more=Ver máis historias
pocket_more_reccommendations=Máis recomendacións
pocket_learn_more=Máis información
pocket_how_it_works=Como funciona
pocket_cta_button=Obter Pocket
pocket_cta_text=Garde no Pocket as historias que lle gusten, e alimente a súa imaxinación con lecturas fascinantes.

Просмотреть файл

@ -58,7 +58,7 @@ menu_action_open_file=ფაილის გახსნა
# LOCALIZATION NOTE (menu_action_copy_download_link, menu_action_go_to_download_page):
# "Download" here, in both cases, is not a verb, it is a noun. As in, "Copy the
# link that belongs to this downloaded item"
menu_action_copy_download_link=ჩამოტვირთვის ბმულის დაკოპირება
menu_action_copy_download_link=ჩამოტვირთვის ბმულის ასლი
menu_action_go_to_download_page=გადასვლა ჩამოტვირთვის გვერდზე
menu_action_remove_download=ისტორიიდან ამოშლა

Просмотреть файл

@ -143,7 +143,9 @@ pocket_read_more=Tèmas populars:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Veire mai darticles
pocket_more_reccommendations=Mai de recomandacions
pocket_learn_more=Ne saber mai
pocket_cta_button=Installar Pocket
highlights_empty_state=Començatz de navegar e aquí vos mostrarem los melhors articles, vidèos e autras paginas quavètz visitadas o apondudas als marcapaginas.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,

Просмотреть файл

@ -1,4 +1,4 @@
newtab_page_title=Tab mới
newtab_page_title=Thẻ mới
header_top_sites=Trang web hàng đầu
header_highlights=Nổi bật
@ -9,9 +9,11 @@ header_recommended_by=Được đề nghị bởi {provider}
# LOCALIZATION NOTE(context_menu_button_sr): This is for screen readers when
# the context menu button is focused/active. Title is the label or hostname of
# the site.
context_menu_button_sr=Mở bảng chọn ngữ cảnh cho {title}
# LOCALIZATION NOTE(section_context_menu_button_sr): This is for screen readers when
# the section edit context menu button is focused/active.
section_context_menu_button_sr=Mở bảng chọn phần ngữ cảnh
# LOCALIZATION NOTE (type_label_*): These labels are associated to pages to give
# context on how the element is related to the user, e.g. type indicates that
@ -47,6 +49,7 @@ menu_action_archive_pocket=Lưu trữ trong Pocket
# found in the context menu of an item that has been downloaded. The intention behind
# "this action" is that it will show where the downloaded file exists on the file system
# for each operating system.
menu_action_show_file_mac_os=Hiển thị trong Finder
menu_action_show_file_windows=Mở thư mục chứa
menu_action_show_file_linux=Mở thư mục chứa
menu_action_show_file_default=Hiện tập tin
@ -91,14 +94,18 @@ prefs_home_description=Chọn nội dung mà bạn muốn thêm vào trang chủ
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
prefs_section_rows_option={num} hàng
prefs_search_header=Tìm kiếm web
prefs_topsites_description=Những trang bạn truy cập nhiều nhất
prefs_topstories_description2=Nội dung tuyệt vời từ trên web, được cá nhân hóa cho bạn
prefs_topstories_options_sponsored_label=Bài viết quảng cáo
prefs_topstories_sponsored_learn_more=Tìm hiểu thêm
prefs_highlights_description=Một lựa chọn các trang web mà bạn đã lưu hoặc truy cập
prefs_highlights_options_visited_label=Trang đã xem
prefs_highlights_options_download_label=Tải xuống gần đây nhất
prefs_highlights_options_pocket_label=Trang đã được lưu vào Pocket
prefs_snippets_description=Cập nhật từ Mozilla và Firefox
settings_pane_button_label=Tùy biến trang Tab mới
settings_pane_button_label=Tùy biến trang Thẻ mới
settings_pane_topsites_header=Các trang Web hàng đầu
settings_pane_highlights_header=Nổi bật
settings_pane_highlights_options_bookmarks=Trang đánh dấu
@ -136,14 +143,17 @@ pocket_read_more=Các chủ đề phổ biến:
# LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
# end of the list of popular topic links.
pocket_read_even_more=Xem nhiều câu chuyện hơn
pocket_more_reccommendations=Nhiều khuyến nghị hơn
pocket_learn_more=Tìm hiểu thêm
pocket_how_it_works=Nó hoạt động như thế nào
pocket_cta_button=Nhận Pocket
pocket_cta_text=Lưu những câu chuyện bạn yêu thích trong Pocket và vui vẻ khi đọc chúng.
highlights_empty_state=Bắt đầu duyệt web và chúng tôi sẽ hiển thị một số bài báo, video, và các trang khác mà bạn vừa truy cập hoặc đã đánh dấu tại đây.
# LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
# in the space that would have shown a few stories, this is shown instead.
# {provider} is replaced by the name of the content provider for this section.
topstories_empty_state=Bạn đã bắt kịp. Kiểm tra lại sau để biết thêm các câu chuyện hàng đầu từ {provider}. Không muốn đợi? Chọn một chủ đề phổ biến để tìm thêm những câu chuyện tuyệt vời từ khắp nơi trên web.
# LOCALIZATION NOTE (manual_migration_explanation2): This message is shown to encourage users to
# import their browser profile from another browser they might be using.
@ -186,13 +196,16 @@ firstrun_form_header=Nhập email của bạn
firstrun_form_sub_header=để tiếp tục với Trình đồng bộ Firefox
firstrun_email_input_placeholder=Email
firstrun_invalid_input=Yêu cầu email hợp lệ
# LOCALIZATION NOTE (firstrun_extra_legal_links): {terms} is equal to firstrun_terms_of_service, and
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
firstrun_extra_legal_links=Bằng cách tiếp tục, bạn đồng ý với {terms} và {privacy}.
firstrun_terms_of_service=Điều khoản dịch vụ
firstrun_privacy_notice=Thông báo bảo mật
firstrun_continue_to_login=Tiếp tục
firstrun_skip_login=Bỏ qua bước này
# LOCALIZATION NOTE (context_menu_title): Action tooltip to open a context menu
context_menu_title=Mở bảng chọn

Просмотреть файл

@ -23,4 +23,5 @@ cd /mozilla-central && ./mach build \
browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js \
browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_1.js \
&& ! grep -q TEST-UNEXPECTED test_run_log \
&& ! ./mach test all_files_referenced | grep activity-stream
&& ! ./mach test all_files_referenced | grep activity-stream \
&& RUN_FIND_DUPES=1 ./mach package

623
browser/components/newtab/package-lock.json сгенерированный
Просмотреть файл

@ -123,6 +123,30 @@
"integrity": "sha512-gqmspPZOMW3MIRb9HlrnbZHXI1/KHTOroBwN1NcLL6pWxzqzEKGvRTq0W/PxS45OtQGbaFikSQpkS5zbnsQm2w==",
"dev": true
},
"@babel/polyfill": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.0.0.tgz",
"integrity": "sha512-dnrMRkyyr74CRelJwvgnnSUDh2ge2NCTyHVwpOdvRMHtJUyxLtMAfhBN3s64pY41zdw0kgiLPh6S20eb1NcX6Q==",
"dev": true,
"requires": {
"core-js": "^2.5.7",
"regenerator-runtime": "^0.11.1"
},
"dependencies": {
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
"dev": true
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
}
}
},
"@babel/runtime": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.2.tgz",
@ -509,9 +533,9 @@
}
},
"acorn": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
"integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
"dev": true
},
"acorn-dynamic-import": {
@ -521,8 +545,22 @@
"dev": true,
"requires": {
"acorn": "^5.0.0"
},
"dependencies": {
"acorn": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
"dev": true
}
}
},
"acorn-jsx": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
"integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
"dev": true
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
@ -558,12 +596,6 @@
}
}
},
"ajv-keywords": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
"dev": true
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
@ -890,6 +922,32 @@
"source-map": "^0.5.7"
}
},
"babel-eslint": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz",
"integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/parser": "^7.0.0",
"@babel/traverse": "^7.0.0",
"@babel/types": "^7.0.0",
"eslint-scope": "3.7.1",
"eslint-visitor-keys": "^1.0.0"
},
"dependencies": {
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
"integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
}
}
},
"babel-generator": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
@ -1154,31 +1212,6 @@
"babel-types": "^6.24.1"
}
},
"babel-polyfill": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
"dev": true,
"requires": {
"babel-runtime": "^6.26.0",
"core-js": "^2.5.0",
"regenerator-runtime": "^0.10.5"
},
"dependencies": {
"core-js": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz",
"integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=",
"dev": true
},
"regenerator-runtime": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
"dev": true
}
}
},
"babel-preset-flow": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz",
@ -1825,9 +1858,9 @@
}
},
"chardet": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"check-error": {
@ -3102,57 +3135,69 @@
}
},
"eslint": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
"integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.9.0.tgz",
"integrity": "sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w==",
"dev": true,
"requires": {
"ajv": "^5.3.0",
"babel-code-frame": "^6.22.0",
"@babel/code-frame": "^7.0.0",
"ajv": "^6.5.3",
"chalk": "^2.1.0",
"concat-stream": "^1.6.0",
"cross-spawn": "^5.1.0",
"debug": "^3.1.0",
"cross-spawn": "^6.0.5",
"debug": "^4.0.1",
"doctrine": "^2.1.0",
"eslint-scope": "^3.7.1",
"eslint-scope": "^4.0.0",
"eslint-utils": "^1.3.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^3.5.4",
"esquery": "^1.0.0",
"espree": "^4.0.0",
"esquery": "^1.0.1",
"esutils": "^2.0.2",
"file-entry-cache": "^2.0.0",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"globals": "^11.0.1",
"ignore": "^3.3.3",
"globals": "^11.7.0",
"ignore": "^4.0.6",
"imurmurhash": "^0.1.4",
"inquirer": "^3.0.6",
"is-resolvable": "^1.0.0",
"js-yaml": "^3.9.1",
"inquirer": "^6.1.0",
"is-resolvable": "^1.1.0",
"js-yaml": "^3.12.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.4",
"minimatch": "^3.0.2",
"lodash": "^4.17.5",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"pluralize": "^7.0.0",
"progress": "^2.0.0",
"regexpp": "^1.0.1",
"regexpp": "^2.0.1",
"require-uncached": "^1.0.3",
"semver": "^5.3.0",
"semver": "^5.5.1",
"strip-ansi": "^4.0.0",
"strip-json-comments": "~2.0.1",
"table": "4.0.2",
"text-table": "~0.2.0"
"strip-json-comments": "^2.0.1",
"table": "^5.0.2",
"text-table": "^0.2.0"
},
"dependencies": {
"acorn": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
"integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
"dev": true
},
"ajv": {
"version": "6.5.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz",
"integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
@ -3179,48 +3224,49 @@
"supports-color": "^5.3.0"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"ms": "2.0.0"
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"espree": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz",
"integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==",
"dev": true,
"requires": {
"acorn": "^5.5.0",
"acorn-jsx": "^3.0.0"
},
"dependencies": {
"acorn-jsx": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
"integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
"dev": true,
"requires": {
"acorn": "^3.0.4"
},
"dependencies": {
"acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
"dev": true
}
}
}
"acorn": "^6.0.2",
"acorn-jsx": "^5.0.0",
"eslint-visitor-keys": "^1.0.0"
}
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"globals": {
"version": "11.5.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz",
"integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==",
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz",
"integrity": "sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==",
"dev": true
},
"has-flag": {
@ -3229,6 +3275,46 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true
},
"js-yaml": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@ -3239,9 +3325,9 @@
}
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
@ -3260,9 +3346,9 @@
},
"dependencies": {
"resolve": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
"dev": true,
"requires": {
"path-parse": "^1.0.5"
@ -3311,9 +3397,9 @@
}
},
"eslint-plugin-import": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.11.0.tgz",
"integrity": "sha1-Fa7qN6Z0mdhI6OmBgG1GJ7VQOBY=",
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz",
"integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==",
"dev": true,
"requires": {
"contains-path": "^0.1.0",
@ -3381,9 +3467,9 @@
}
},
"resolve": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
"dev": true,
"requires": {
"path-parse": "^1.0.5"
@ -3407,9 +3493,9 @@
}
},
"eslint-plugin-mozilla": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-mozilla/-/eslint-plugin-mozilla-0.16.0.tgz",
"integrity": "sha512-8yYv9zGVa3qpnSk1aiG1nrNHW3GrYV/AJXSNm9RnfLg6tudOMVSXsTjfHsCzC2+i4pJBechpnL5SNKgTOZ9Mdw==",
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-mozilla/-/eslint-plugin-mozilla-0.16.3.tgz",
"integrity": "sha512-ZvseeV3flenYTZP/B6SUTnwmfMkW99o0FNm3DaM4ZlyahTGXHepmAEPLkwIvY5eKrcRmo8On+kNmUqelDmxwKw==",
"dev": true,
"requires": {
"htmlparser2": "3.9.2",
@ -3418,39 +3504,57 @@
}
},
"eslint-plugin-no-unsanitized": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-3.0.0.tgz",
"integrity": "sha1-FEi1LN14cfF0PCkAEXuwh8/nuAY=",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-3.0.2.tgz",
"integrity": "sha512-JnwpoH8Sv4QOjrTDutENBHzSnyYtspdjtglYtqUtAHe6f6LLKqykJle+UwFPg23GGwt5hI3amS9CRDezW8GAww==",
"dev": true
},
"eslint-plugin-promise": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz",
"integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz",
"integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==",
"dev": true
},
"eslint-plugin-react": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz",
"integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==",
"version": "7.11.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz",
"integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==",
"dev": true,
"requires": {
"doctrine": "^2.0.2",
"has": "^1.0.1",
"array-includes": "^3.0.3",
"doctrine": "^2.1.0",
"has": "^1.0.3",
"jsx-ast-utils": "^2.0.1",
"prop-types": "^15.6.0"
"prop-types": "^15.6.2"
},
"dependencies": {
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
}
}
},
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
"integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
"integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
"dev": true
},
"eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -3458,12 +3562,12 @@
"dev": true
},
"eslint-watch": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/eslint-watch/-/eslint-watch-3.1.4.tgz",
"integrity": "sha512-UpEszJuz/FBG3usSJkCy1p4iWMWmXFyMiCBnnJ9l1wXtMpEg6+mjnWM1MbStY5/QhuHEvW4M3AAadjUVv6FrAQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/eslint-watch/-/eslint-watch-4.0.2.tgz",
"integrity": "sha512-kbso5+pd6tIwmnTidQfEQ5nRydYw4+8I+8h19yIG/RWcRi8H4TCLlBHwAFBDAmLE4dTkPkctpQQSP8x1nMyYqw==",
"dev": true,
"requires": {
"babel-polyfill": "^6.20.0",
"@babel/polyfill": "^7.0.0-beta.51",
"bluebird": "^3.5.1",
"chalk": "^2.1.0",
"chokidar": "^2.0.0",
@ -3555,32 +3659,41 @@
}
},
"chokidar": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz",
"integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
"integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.0",
"braces": "^2.3.0",
"fsevents": "^1.1.2",
"fsevents": "^1.2.2",
"glob-parent": "^3.1.0",
"inherits": "^2.0.1",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"lodash.debounce": "^4.0.8",
"normalize-path": "^2.1.1",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.0.0",
"upath": "^1.0.0"
"upath": "^1.0.5"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "2.0.0"
"ms": "^2.1.1"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
}
}
},
"expand-brackets": {
@ -3874,9 +3987,9 @@
"dev": true
},
"source-map-support": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.5.tgz",
"integrity": "sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA==",
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
"integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@ -3893,9 +4006,9 @@
}
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
@ -3913,6 +4026,12 @@
"acorn-jsx": "^3.0.0"
},
"dependencies": {
"acorn": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
"dev": true
},
"acorn-jsx": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
@ -3979,22 +4098,6 @@
"es5-ext": "~0.10.14"
}
},
"event-stream": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz",
"integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==",
"dev": true,
"requires": {
"duplexer": "^0.1.1",
"flatmap-stream": "^0.1.0",
"from": "^0.1.7",
"map-stream": "0.0.7",
"pause-stream": "^0.0.11",
"split": "^1.0.1",
"stream-combiner": "^0.2.2",
"through": "^2.3.8"
}
},
"eventemitter3": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
@ -4155,14 +4258,25 @@
}
},
"external-editor": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
"integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
"dev": true,
"requires": {
"chardet": "^0.4.0",
"iconv-lite": "^0.4.17",
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"extglob": {
@ -4366,12 +4480,6 @@
"write": "^0.2.1"
}
},
"flatmap-stream": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.1.tgz",
"integrity": "sha512-lAq4tLbm3sidmdCN8G3ExaxH7cUCtP5mgDvrYowsx84dcYkJJ4I28N7gkxA6+YlSXzaGLJYIDEi9WGfXzMiXdw==",
"dev": true
},
"fluent": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/fluent/-/fluent-0.6.4.tgz",
@ -4463,12 +4571,6 @@
"map-cache": "^0.2.2"
}
},
"from": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
"dev": true
},
"from2": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@ -5955,22 +6057,21 @@
"dev": true
},
"inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
"integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz",
"integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
"dev": true,
"requires": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.0",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^2.0.4",
"external-editor": "^3.0.0",
"figures": "^2.0.0",
"lodash": "^4.3.0",
"lodash": "^4.17.10",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rx-lite": "^4.0.8",
"rx-lite-aggregates": "^4.0.8",
"rxjs": "^6.1.0",
"string-width": "^2.1.0",
"strip-ansi": "^4.0.0",
"through": "^2.3.6"
@ -6018,9 +6119,9 @@
}
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
@ -7861,12 +7962,6 @@
"integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
"dev": true
},
"map-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
"integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
"dev": true
},
"map-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@ -8462,17 +8557,17 @@
}
},
"npm-run-all": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.3.tgz",
"integrity": "sha512-aOG0N3Eo/WW+q6sUIdzcV2COS8VnTZCmdji0VQIAZF3b+a3YWb0AD0vFIyjKec18A7beLGbaQ5jFTNI2bPt9Cg==",
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
"integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"chalk": "^2.1.0",
"cross-spawn": "^6.0.4",
"ansi-styles": "^3.2.1",
"chalk": "^2.4.1",
"cross-spawn": "^6.0.5",
"memorystream": "^0.3.1",
"minimatch": "^3.0.4",
"ps-tree": "^1.1.0",
"pidtree": "^0.3.0",
"read-pkg": "^3.0.0",
"shell-quote": "^1.6.1",
"string.prototype.padend": "^3.0.0"
@ -9080,15 +9175,6 @@
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
"dev": true
},
"pause-stream": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
"integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
"dev": true,
"requires": {
"through": "~2.3"
}
},
"pbkdf2": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
@ -9342,6 +9428,12 @@
}
}
},
"pidtree": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
"integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
"dev": true
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@ -9444,9 +9536,9 @@
"dev": true
},
"progress": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz",
"integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==",
"dev": true
},
"promise": {
@ -9487,15 +9579,6 @@
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
"ps-tree": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz",
"integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=",
"dev": true,
"requires": {
"event-stream": "~3.3.0"
}
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@ -9958,9 +10041,9 @@
}
},
"regexpp": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
"integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"remove-trailing-separator": {
@ -10189,19 +10272,13 @@
"aproba": "^1.1.1"
}
},
"rx-lite": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
"dev": true
},
"rx-lite-aggregates": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
"rxjs": {
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
"integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
"dev": true,
"requires": {
"rx-lite": "*"
"tslib": "^1.9.0"
}
},
"safe-buffer": {
@ -11409,15 +11486,6 @@
"integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
"dev": true
},
"split": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
"integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
"dev": true,
"requires": {
"through": "2"
}
},
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@ -11520,16 +11588,6 @@
"readable-stream": "^2.0.2"
}
},
"stream-combiner": {
"version": "0.2.2",
"resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
"integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
"dev": true,
"requires": {
"duplexer": "~0.1.1",
"through": "~2.3.4"
}
},
"stream-each": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
@ -11744,53 +11802,40 @@
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
"table": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/table/-/table-5.1.0.tgz",
"integrity": "sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg==",
"dev": true,
"requires": {
"ajv": "^5.2.3",
"ajv-keywords": "^2.1.0",
"chalk": "^2.1.0",
"lodash": "^4.17.4",
"ajv": "^6.5.3",
"lodash": "^4.17.10",
"slice-ansi": "1.0.0",
"string-width": "^2.1.1"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"ajv": {
"version": "6.5.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz",
"integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"chalk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}
}
},

Просмотреть файл

@ -17,7 +17,9 @@
},
"devDependencies": {
"@octokit/rest": "15.17.0",
"acorn": "6.0.4",
"babel-core": "6.26.3",
"babel-eslint": "10.0.1",
"babel-loader": "7.1.4",
"babel-plugin-jsm-to-commonjs": "0.4.0",
"babel-plugin-jsm-to-esmodules": "0.4.0",
@ -33,14 +35,14 @@
"cpx": "1.5.0",
"enzyme": "3.7.0",
"enzyme-adapter-react-16": "1.7.0",
"eslint": "4.19.1",
"eslint-plugin-import": "2.11.0",
"eslint": "5.9.0",
"eslint-plugin-import": "2.14.0",
"eslint-plugin-json": "1.2.1",
"eslint-plugin-mozilla": "0.16.0",
"eslint-plugin-no-unsanitized": "3.0.0",
"eslint-plugin-promise": "3.7.0",
"eslint-plugin-react": "7.7.0",
"eslint-watch": "3.1.4",
"eslint-plugin-mozilla": "0.16.3",
"eslint-plugin-no-unsanitized": "3.0.2",
"eslint-plugin-promise": "4.0.1",
"eslint-plugin-react": "7.11.1",
"eslint-watch": "4.0.2",
"husky": "1.1.3",
"istanbul-instrumenter-loader": "3.0.1",
"joi-browser": "13.4.0",
@ -59,7 +61,7 @@
"mock-raf": "1.0.1",
"node-fetch": "2.2.1",
"node-sass": "4.10.0",
"npm-run-all": "4.1.3",
"npm-run-all": "4.1.5",
"pontoon-to-json": "2.0.0",
"prop-types": "15.6.2",
"raw-loader": "0.5.1",

Просмотреть файл

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "المواضيع الشائعة:",
"pocket_read_even_more": "اعرض المزيد من الأخبار",
"pocket_more_reccommendations": "مقترحات أخرى",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "آلية العمل",
"pocket_cta_button": "نزِّل بوكِت",
"pocket_cta_text": "احفظ القصص التي تحبّها في بوكِت، وزوّد عقلك بمقالات رائعة.",
"highlights_empty_state": "ابدأ التصفح وسنعرض أمامك بعض المقالات والفيديوهات والمواقع الأخرى التي زرتها حديثا أو أضفتها إلى العلامات هنا.",

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -75,7 +75,7 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Beliebte Themen:",
"pocket_read_even_more": "Weitere Nachrichten ansehen",
"pocket_more_reccommendations": "Mehr Empfehlungen",
"pocket_how_it_works": "How it works",
"pocket_how_it_works": "Wie es funktioniert",
"pocket_cta_button": "Pocket holen",
"pocket_cta_text": "Speichern Sie Ihre Lieblingstexte in Pocket und gewinnen Sie gedankenreiche Einblicke durch faszinierende Texte.",
"highlights_empty_state": "Surfen Sie los und wir zeigen Ihnen hier einige der interessanten Artikel, Videos und anderen Seiten, die Sie kürzlich besucht oder als Lesezeichen gespeichert haben.",
@ -107,6 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Datenschutzhinweis",
"firstrun_continue_to_login": "Weiter",
"firstrun_skip_login": "Diesen Schritt überspringen",
"context_menu_title": "Open menu",
"context_menu_title": "Menü öffnen",
"pocket_learn_more": "Weitere Informationen"
};

Просмотреть файл

@ -107,5 +107,6 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Política de privacidade",
"firstrun_continue_to_login": "Continuar",
"firstrun_skip_login": "Ignorar este paso",
"context_menu_title": "Abrir menú"
"context_menu_title": "Abrir menú",
"pocket_learn_more": "Máis información"
};

Просмотреть файл

@ -29,7 +29,7 @@ window.gActivityStreamStrings = {
"menu_action_show_file_linux": "შემცველი საქაღალდის გახსნა",
"menu_action_show_file_default": "ფაილის ჩვენება",
"menu_action_open_file": "ფაილის გახსნა",
"menu_action_copy_download_link": "ჩამოტვირთვის ბმულის დაკოპირება",
"menu_action_copy_download_link": "ჩამოტვირთვის ბმულის ასლი",
"menu_action_go_to_download_page": "გადასვლა ჩამოტვირთვის გვერდზე",
"menu_action_remove_download": "ისტორიიდან ამოშლა",
"search_button": "ძიება",

Просмотреть файл

@ -74,9 +74,9 @@ window.gActivityStreamStrings = {
"topsites_form_image_validation": "Limatge a pas capitat de se cargar. Ensajatz una URL diferenta.",
"pocket_read_more": "Tèmas populars:",
"pocket_read_even_more": "Veire mai darticles",
"pocket_more_reccommendations": "More Recommendations",
"pocket_more_reccommendations": "Mai de recomandacions",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_button": "Installar Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"highlights_empty_state": "Començatz de navegar e aquí vos mostrarem los melhors articles, vidèos e autras paginas quavètz visitadas o apondudas als marcapaginas.",
"topstories_empty_state": "Youve caught up. Check back later for more top stories from {provider}. Cant wait? Select a popular topic to find more great stories from around the web.",

Просмотреть файл

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<title>Tab mới</title>
<title>Thẻ mới</title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,11 +1,11 @@
// Note - this is a generated vi file.
window.gActivityStreamStrings = {
"newtab_page_title": "Tab mới",
"newtab_page_title": "Thẻ mới",
"header_top_sites": "Trang web hàng đầu",
"header_highlights": "Nổi bật",
"header_recommended_by": "Được đề nghị bởi {provider}",
"context_menu_button_sr": "Open context menu for {title}",
"section_context_menu_button_sr": "Open the section context menu",
"context_menu_button_sr": "Mở bảng chọn ngữ cảnh cho {title}",
"section_context_menu_button_sr": "Mở bảng chọn phần ngữ cảnh",
"type_label_visited": "Đã truy cập",
"type_label_bookmarked": "Đã được đánh dấu",
"type_label_recommended": "Xu hướng",
@ -24,7 +24,7 @@ window.gActivityStreamStrings = {
"menu_action_save_to_pocket": "Lưu vào Pocket",
"menu_action_delete_pocket": "Xóa khỏi Pocket",
"menu_action_archive_pocket": "Lưu trữ trong Pocket",
"menu_action_show_file_mac_os": "Show in Finder",
"menu_action_show_file_mac_os": "Hiển thị trong Finder",
"menu_action_show_file_windows": "Mở thư mục chứa",
"menu_action_show_file_linux": "Mở thư mục chứa",
"menu_action_show_file_default": "Hiện tập tin",
@ -40,18 +40,18 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_buttontext": "Ok, đã hiểu",
"prefs_home_header": "Nội dung trang chủ của Firefox",
"prefs_home_description": "Chọn nội dung mà bạn muốn thêm vào trang chủ của Firefox.",
"prefs_section_rows_option": "{num} row;{num} rows",
"prefs_section_rows_option": "{num} hàng",
"prefs_search_header": "Tìm kiếm web",
"prefs_topsites_description": "Những trang bạn truy cập nhiều nhất",
"prefs_topstories_description2": "Great content from around the web, personalized for you",
"prefs_topstories_options_sponsored_label": "Sponsored Stories",
"prefs_topstories_description2": "Nội dung tuyệt vời từ trên web, được cá nhân hóa cho bạn",
"prefs_topstories_options_sponsored_label": "Bài viết quảng cáo",
"prefs_topstories_sponsored_learn_more": "Tìm hiểu thêm",
"prefs_highlights_description": "Một lựa chọn các trang web mà bạn đã lưu hoặc truy cập",
"prefs_highlights_options_visited_label": "Trang đã xem",
"prefs_highlights_options_download_label": "Tải xuống gần đây nhất",
"prefs_highlights_options_pocket_label": "Pages Saved to Pocket",
"prefs_highlights_options_pocket_label": "Trang đã được lưu vào Pocket",
"prefs_snippets_description": "Cập nhật từ Mozilla và Firefox",
"settings_pane_button_label": "Tùy biến trang Tab mới",
"settings_pane_button_label": "Tùy biến trang Thẻ mới",
"settings_pane_topsites_header": "Các trang Web hàng đầu",
"settings_pane_highlights_header": "Nổi bật",
"settings_pane_highlights_options_bookmarks": "Trang đánh dấu",
@ -75,11 +75,11 @@ window.gActivityStreamStrings = {
"pocket_read_more": "Các chủ đề phổ biến:",
"pocket_read_even_more": "Xem nhiều câu chuyện hơn",
"pocket_more_reccommendations": "Nhiều khuyến nghị hơn",
"pocket_how_it_works": "How it works",
"pocket_cta_button": "Get Pocket",
"pocket_cta_text": "Save the stories you love in Pocket, and fuel your mind with fascinating reads.",
"pocket_how_it_works": "Nó hoạt động như thế nào",
"pocket_cta_button": "Nhận Pocket",
"pocket_cta_text": "Lưu những câu chuyện bạn yêu thích trong Pocket và vui vẻ khi đọc chúng.",
"highlights_empty_state": "Bắt đầu duyệt web và chúng tôi sẽ hiển thị một số bài báo, video, và các trang khác mà bạn vừa truy cập hoặc đã đánh dấu tại đây.",
"topstories_empty_state": "Youve caught up. Check back later for more top stories from {provider}. Cant wait? Select a popular topic to find more great stories from around the web.",
"topstories_empty_state": "Bạn đã bắt kịp. Kiểm tra lại sau để biết thêm các câu chuyện hàng đầu từ {provider}. Không muốn đợi? Chọn một chủ đề phổ biến để tìm thêm những câu chuyện tuyệt vời từ khắp nơi trên web.",
"manual_migration_explanation2": "Thử Firefox với trang đánh dấu, lịch sử và mật khẩu từ trình duyệt khác.",
"manual_migration_cancel_button": "Không, cảm ơn",
"manual_migration_import_button": "Nhập ngay bây giờ",
@ -102,11 +102,11 @@ window.gActivityStreamStrings = {
"firstrun_form_sub_header": "để tiếp tục với Trình đồng bộ Firefox",
"firstrun_email_input_placeholder": "Email",
"firstrun_invalid_input": "Yêu cầu email hợp lệ",
"firstrun_extra_legal_links": "By proceeding, you agree to the {terms} and {privacy}.",
"firstrun_extra_legal_links": "Bằng cách tiếp tục, bạn đồng ý với {terms} và {privacy}.",
"firstrun_terms_of_service": "Điều khoản dịch vụ",
"firstrun_privacy_notice": "Thông báo bảo mật",
"firstrun_continue_to_login": "Tiếp tục",
"firstrun_skip_login": "Bỏ qua bước này",
"context_menu_title": "Open menu",
"context_menu_title": "Mở bảng chọn",
"pocket_learn_more": "Tìm hiểu thêm"
};

Просмотреть файл

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
<title>Tab mới</title>
<title>Thẻ mới</title>
<link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />

Просмотреть файл

@ -7,8 +7,7 @@
const Services = require("Services");
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { createFactory } =
require("devtools/client/shared/vendor/react");
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render, unmountComponentAtNode } =
require("devtools/client/shared/vendor/react-dom");
const Provider =
@ -37,10 +36,9 @@ const {
loader.lazyRequireGetter(this, "adbAddon", "devtools/shared/adb/adb-addon", true);
const Router = createFactory(require("devtools/client/shared/vendor/react-router-dom").HashRouter);
const App = createFactory(require("./src/components/App"));
const { PAGES, RUNTIMES } = require("./src/constants");
const AboutDebugging = {
async init() {
if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
@ -59,11 +57,22 @@ const AboutDebugging = {
await l10n.init();
render(
Provider({ store: this.store }, App({ fluentBundles: l10n.getBundles() })),
Provider(
{
store: this.store,
},
Router(
{},
App(
{
fluentBundles: l10n.getBundles(),
}
)
)
),
this.mount
);
this.actions.selectPage(PAGES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX);
this.actions.updateNetworkLocations(getNetworkLocations());
addNetworkLocationsObserver(this.onNetworkLocationsUpdated);
@ -99,6 +108,9 @@ const AboutDebugging = {
await this.actions.unwatchRuntime(currentRuntimeId);
}
// Remove all client listeners.
this.actions.removeRuntimeListeners();
removeNetworkLocationsObserver(this.onNetworkLocationsUpdated);
removeUSBRuntimesObserver(this.onUSBRuntimesUpdated);
disableUSBRuntimes();

Просмотреть файл

@ -6,6 +6,8 @@
const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
const { l10n } = require("../modules/l10n");
@ -44,9 +46,10 @@ function inspectDebugTarget(type, id) {
case DEBUG_TARGETS.TAB: {
// Open tab debugger in new window.
if (runtimeType === RUNTIMES.NETWORK || runtimeType === RUNTIMES.USB) {
const { host, port } = runtimeDetails.transportDetails;
window.open(`about:devtools-toolbox?type=tab&id=${id}` +
`&host=${host}&port=${port}`);
// Pass the remote id from the client manager so that about:devtools-toolbox can
// retrieve the connected client directly.
const remoteId = remoteClientManager.getRemoteId(runtime.id, runtime.type);
window.open(`about:devtools-toolbox?type=tab&id=${id}&remoteId=${remoteId}`);
} else if (runtimeType === RUNTIMES.THIS_FIREFOX) {
window.open(`about:devtools-toolbox?type=tab&id=${id}`);
}

Просмотреть файл

@ -16,6 +16,9 @@ const { isSupportedDebugTarget } = require("../modules/debug-target-support");
const { createClientForRuntime } = require("../modules/runtime-client-factory");
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
const {
CONNECT_RUNTIME_FAILURE,
CONNECT_RUNTIME_START,
@ -68,7 +71,7 @@ function connectRuntime(id) {
dispatch({ type: CONNECT_RUNTIME_START });
try {
const runtime = findRuntimeById(id, getState().runtimes);
const { clientWrapper, transportDetails } = await createClientForRuntime(runtime);
const clientWrapper = await createClientForRuntime(runtime);
const info = await getRuntimeInfo(runtime, clientWrapper);
const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
@ -77,7 +80,6 @@ function connectRuntime(id) {
clientWrapper,
connectionPromptEnabled,
info,
transportDetails,
};
if (runtime.type === RUNTIMES.USB) {
@ -215,6 +217,7 @@ function updateUSBRuntimes(runtimes) {
// Current runtime can not be retrieved after USB_RUNTIMES_UPDATED action, since
// that updates runtime state. So, before that we fire selectPage action so that to
// transact unwatchRuntime correctly.
await dispatch(Actions.selectPage(RUNTIMES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX));
}
@ -228,12 +231,38 @@ function updateUSBRuntimes(runtimes) {
}
dispatch({ type: USB_RUNTIMES_UPDATED, runtimes });
for (const runtime of getState().runtimes.usbRuntimes) {
const isConnected = !!runtime.runtimeDetails;
const hasConnectedClient = remoteClientManager.hasClient(runtime.id, runtime.type);
if (!isConnected && hasConnectedClient) {
await dispatch(connectRuntime(runtime.id));
}
}
};
}
/**
* Remove all the listeners added on client objects. Since those objects are persisted
* regardless of the about:debugging lifecycle, all the added events should be removed
* before leaving about:debugging.
*/
function removeRuntimeListeners() {
return (dispatch, getState) => {
const { usbRuntimes } = getState().runtimes;
for (const runtime of usbRuntimes) {
if (runtime.runtimeDetails) {
const { clientWrapper } = runtime.runtimeDetails;
clientWrapper.removeListener("closed", onUSBDebuggerClientClosed);
}
}
};
}
module.exports = {
connectRuntime,
disconnectRuntime,
removeRuntimeListeners,
unwatchRuntime,
updateConnectionPromptSetting,
updateUSBRuntimes,

Просмотреть файл

@ -15,7 +15,7 @@ const {
DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
NETWORK_LOCATIONS_UPDATED,
PAGE_SELECTED,
PAGES,
PAGE_TYPES,
USB_RUNTIMES_SCAN_START,
USB_RUNTIMES_SCAN_SUCCESS,
} = require("../constants");
@ -26,32 +26,35 @@ const { refreshUSBRuntimes } = require("../modules/usb-runtimes");
const Actions = require("./index");
// XXX: Isolating the code here, because it feels wrong to rely solely on the page "not"
// being CONNECT to decide what to do. Should we have a page "type" on top of page "id"?
function _isRuntimePage(page) {
return page && page !== PAGES.CONNECT;
}
function selectPage(page, runtimeId) {
return async (dispatch, getState) => {
const isSamePage = (oldPage, newPage) => {
if (newPage === PAGE_TYPES.RUNTIME && oldPage === PAGE_TYPES.RUNTIME) {
return runtimeId === getState().runtimes.selectedRuntimeId;
}
return newPage === oldPage;
};
const currentPage = getState().ui.selectedPage;
// Nothing to dispatch if the page is the same as the current page.
if (page === currentPage) {
// Nothing to dispatch if the page is the same as the current page, or
// if we are not providing any page.
// Note: maybe we should have a PAGE_SELECTED_FAILURE action for proper logging
if (!page || isSamePage(currentPage, page)) {
return;
}
// Stop watching current runtime, if currently on a DEVICE or THIS_FIREFOX page.
if (_isRuntimePage(currentPage)) {
// Stop watching current runtime, if currently on a RUNTIME page.
if (currentPage === PAGE_TYPES.RUNTIME) {
const currentRuntimeId = getState().runtimes.selectedRuntimeId;
await dispatch(Actions.unwatchRuntime(currentRuntimeId));
}
// Start watching current runtime, if moving to a DEVICE or THIS_FIREFOX page.
if (_isRuntimePage(page)) {
// Start watching current runtime, if moving to a RUNTIME page.
if (page === PAGE_TYPES.RUNTIME) {
await dispatch(Actions.watchRuntime(runtimeId));
}
dispatch({ type: PAGE_SELECTED, page });
dispatch({ type: PAGE_SELECTED, page, runtimeId });
};
}

Просмотреть файл

@ -12,8 +12,13 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const FluentReact = require("devtools/client/shared/vendor/fluent-react");
const LocalizationProvider = createFactory(FluentReact.LocalizationProvider);
const { PAGES } = require("../constants");
const Route = createFactory(require("devtools/client/shared/vendor/react-router-dom").Route);
const Switch = createFactory(require("devtools/client/shared/vendor/react-router-dom").Switch);
const Redirect = createFactory(require("devtools/client/shared/vendor/react-router-dom").Redirect);
const { withRouter } = require("devtools/client/shared/vendor/react-router-dom");
const Types = require("../types/index");
const { RUNTIMES } = require("../constants");
const ConnectPage = createFactory(require("./connect/ConnectPage"));
const RuntimePage = createFactory(require("./RuntimePage"));
@ -33,39 +38,94 @@ class App extends PureComponent {
networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
selectedPage: PropTypes.string,
selectedRuntime: PropTypes.string,
usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
wifiEnabled: PropTypes.bool.isRequired,
};
}
getSelectedPageComponent() {
renderConnect() {
const {
adbAddonStatus,
dispatch,
networkEnabled,
networkLocations,
selectedPage,
wifiEnabled,
} = this.props;
if (!selectedPage) {
// No page selected.
return null;
return ConnectPage({
adbAddonStatus,
dispatch,
networkEnabled,
networkLocations,
wifiEnabled,
});
}
// The `match` object here is passed automatically by the Route object.
// We are using it to read the route path.
// See react-router docs:
// https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/match.md
renderRuntime({ match }) {
// Redirect to This Firefox in these cases:
// - If the runtimepage for a device is the first page shown (since we can't
// keep connections open between page reloads).
// - If no runtimeId is given.
// - If runtime is not found in the runtimes list (this is handled later)
const isDeviceFirstPage =
!this.props.selectedPage &&
match.params.runtimeId !== RUNTIMES.THIS_FIREFOX;
if (!match.params.runtimeId || isDeviceFirstPage) {
return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` });
}
switch (selectedPage) {
case PAGES.CONNECT:
return ConnectPage({
adbAddonStatus,
dispatch,
networkEnabled,
networkLocations,
wifiEnabled,
});
default:
// All pages except for the CONNECT page are RUNTIME pages.
return RuntimePage({ dispatch });
const isRuntimeAvailable = id => {
const runtimes = this.props.usbRuntimes;
return !!runtimes.find(x => x.id === id);
};
const { dispatch } = this.props;
let runtimeId = match.params.runtimeId || RUNTIMES.THIS_FIREFOX;
if (match.params.runtimeId !== RUNTIMES.THIS_FIREFOX) {
const rawId = decodeURIComponent(match.params.runtimeId);
if (isRuntimeAvailable(rawId)) {
runtimeId = rawId;
} else {
// Also redirect to "This Firefox" if runtime is not found
return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` });
}
}
// we need to pass a key so the component updates when we want to showcase
// a different runtime
return RuntimePage({ dispatch, key: runtimeId, runtimeId });
}
renderRoutes() {
return Switch(
{},
Route({
path: "/connect",
render: () => this.renderConnect(),
}),
Route({
path: "/runtime/:runtimeId",
render: routeProps => this.renderRuntime(routeProps),
}),
Route({
path: "/",
exact: true,
// will redirect to This Firefox
render: routeProps => this.renderRuntime(routeProps),
}),
// default route when there's no match
// TODO: maybe we'd like to do something else than a redirect. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1509897
Route({
render: () => Redirect({ to: "/"}),
})
);
}
render() {
@ -76,6 +136,7 @@ class App extends PureComponent {
isScanningUsb,
networkRuntimes,
selectedPage,
selectedRuntime,
usbRuntimes,
} = this.props;
@ -83,21 +144,17 @@ class App extends PureComponent {
{ messages: fluentBundles },
dom.div(
{ className: "app" },
Sidebar(
{
adbAddonStatus,
className: "app__sidebar",
dispatch,
isScanningUsb,
networkRuntimes,
selectedPage,
usbRuntimes,
}
),
dom.main(
{ className: "app__content" },
this.getSelectedPageComponent()
)
Sidebar({
adbAddonStatus,
className: "app__sidebar",
dispatch,
isScanningUsb,
networkRuntimes,
selectedPage,
selectedRuntime,
usbRuntimes,
}),
dom.main({ className: "app__content" }, this.renderRoutes())
)
);
}
@ -111,6 +168,7 @@ const mapStateToProps = state => {
networkLocations: state.ui.networkLocations,
networkRuntimes: state.runtimes.networkRuntimes,
selectedPage: state.ui.selectedPage,
selectedRuntime: state.ui.selectedRuntime,
usbRuntimes: state.runtimes.usbRuntimes,
wifiEnabled: state.ui.wifiEnabled,
};
@ -120,4 +178,4 @@ const mapDispatchToProps = dispatch => ({
dispatch,
});
module.exports = connect(mapStateToProps, mapDispatchToProps)(App);
module.exports = withRouter(connect(mapStateToProps, mapDispatchToProps)(App));

Просмотреть файл

@ -24,7 +24,9 @@ const TemporaryExtensionInstaller =
createFactory(require("./debugtarget/TemporaryExtensionInstaller"));
const WorkerDetail = createFactory(require("./debugtarget/WorkerDetail"));
const { DEBUG_TARGET_PANE } = require("../constants");
const Actions = require("../actions/index");
const { DEBUG_TARGET_PANE, PAGE_TYPES } = require("../constants");
const {
getCurrentConnectionPromptSetting,
getCurrentRuntimeInfo,
@ -39,6 +41,7 @@ class RuntimePage extends PureComponent {
dispatch: PropTypes.func.isRequired,
installedExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
otherWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
runtimeId: PropTypes.string.isRequired,
runtimeInfo: PropTypes.object,
serviceWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
sharedWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
@ -47,6 +50,13 @@ class RuntimePage extends PureComponent {
};
}
// TODO: avoid the use of this method
// https://bugzilla.mozilla.org/show_bug.cgi?id=1508688
componentWillMount() {
const { dispatch, runtimeId } = this.props;
dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, runtimeId));
}
renderConnectionPromptSetting() {
const { connectionPromptEnabled, dispatch } = this.props;

Просмотреть файл

@ -24,7 +24,7 @@ const ConnectSteps = createFactory(require("./ConnectSteps"));
const NetworkLocationsForm = createFactory(require("./NetworkLocationsForm"));
const NetworkLocationsList = createFactory(require("./NetworkLocationsList"));
const { PREFERENCES } = require("../../constants");
const { PREFERENCES, PAGE_TYPES } = require("../../constants");
const USB_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
const WIFI_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
@ -43,6 +43,12 @@ class ConnectPage extends PureComponent {
};
}
// TODO: avoid the use of this method
// https://bugzilla.mozilla.org/show_bug.cgi?id=1508688
componentWillMount() {
this.props.dispatch(Actions.selectPage(PAGE_TYPES.CONNECT));
}
renderWifi() {
const { getString, wifiEnabled } = this.props;
@ -56,27 +62,28 @@ class ConnectPage extends PureComponent {
icon: WIFI_ICON_SRC,
title: "Via WiFi",
},
wifiEnabled ?
ConnectSteps({
steps: [
getString("about-debugging-connect-wifi-step-same-network"),
getString("about-debugging-connect-wifi-step-open-firefox"),
getString("about-debugging-connect-wifi-step-open-options"),
getString("about-debugging-connect-wifi-step-enable-debug"),
],
}) :
Localized(
{
id: "about-debugging-connect-wifi-disabled",
$pref: PREFERENCES.WIFI_ENABLED,
},
dom.div(
wifiEnabled
? ConnectSteps(
{
className: "connect-page__disabled-section",
steps: [
getString("about-debugging-connect-wifi-step-same-network"),
getString("about-debugging-connect-wifi-step-open-firefox"),
getString("about-debugging-connect-wifi-step-open-options"),
getString("about-debugging-connect-wifi-step-enable-debug"),
],
})
: Localized(
{
id: "about-debugging-connect-wifi-disabled",
$pref: PREFERENCES.WIFI_ENABLED,
},
"about-debugging-connect-wifi-disabled"
dom.div(
{
className: "connect-page__disabled-section",
},
"about-debugging-connect-wifi-disabled"
)
)
)
)
);
}
@ -121,8 +128,9 @@ class ConnectPage extends PureComponent {
},
dom.button(
{
className: "default-button connect-page__usb__toggle-button " +
"js-connect-usb-toggle-button",
className:
"default-button connect-page__usb__toggle-button " +
"js-connect-usb-toggle-button",
disabled,
onClick: () => this.onToggleUSBClick(),
},
@ -144,15 +152,17 @@ class ConnectPage extends PureComponent {
icon: USB_ICON_SRC,
title: "Via USB",
},
(isAddonInstalled ?
ConnectSteps({
steps: [
getString("about-debugging-connect-usb-step-enable-dev-menu"),
getString("about-debugging-connect-usb-step-enable-debug"),
getString("about-debugging-connect-usb-step-plug-device"),
],
}) :
Localized(
isAddonInstalled
? ConnectSteps(
{
steps: [
getString("about-debugging-connect-usb-step-enable-dev-menu"),
getString("about-debugging-connect-usb-step-enable-debug"),
getString("about-debugging-connect-usb-step-plug-device"),
],
}
)
: Localized(
{
id: "about-debugging-connect-usb-disabled",
},
@ -161,10 +171,9 @@ class ConnectPage extends PureComponent {
className: "js-connect-usb-disabled-message",
},
"Enabling this will download and add the required Android USB debugging " +
"components to Firefox."
"components to Firefox."
)
)
),
),
this.renderUsbToggleButton()
)
);
@ -184,30 +193,29 @@ class ConnectPage extends PureComponent {
icon: GLOBE_ICON_SRC,
title: "Via Network Location",
},
...(
networkEnabled ?
[
NetworkLocationsList({ dispatch, networkLocations }),
dom.hr({ className: "separator separator--breathe" }),
NetworkLocationsForm({ dispatch }),
] : [
// We are using an array for this single element because of the spread
// operator (...). The spread operator avoids React warnings about missing
// keys.
Localized(
{
id: "about-debugging-connect-network-disabled",
$pref: PREFERENCES.NETWORK_ENABLED,
},
dom.div(
{
className: "connect-page__disabled-section",
},
"about-debugging-connect-network-disabled"
)
),
...(networkEnabled
? [
NetworkLocationsList({ dispatch, networkLocations }),
dom.hr({ className: "separator separator--breathe" }),
NetworkLocationsForm({ dispatch }),
]
)
: [
// We are using an array for this single element because of the spread
// operator (...). The spread operator avoids React warnings about missing
// keys.
Localized(
{
id: "about-debugging-connect-network-disabled",
$pref: PREFERENCES.NETWORK_ENABLED,
},
dom.div(
{
className: "connect-page__disabled-section",
},
"about-debugging-connect-network-disabled"
)
),
])
)
);
}
@ -230,7 +238,7 @@ class ConnectPage extends PureComponent {
),
this.renderWifi(),
this.renderUsb(),
this.renderNetwork(),
this.renderNetwork()
);
}
}

Просмотреть файл

@ -9,3 +9,7 @@
.debug-target-pane__icon--collapsed {
transform: rotate(-90deg);
}
.debug-target-pane__title {
cursor: pointer;
}

Просмотреть файл

@ -50,8 +50,8 @@ class DebugTargetPane extends PureComponent {
},
dom.a(
{
className: "undecorated-link js-debug-target-pane-title",
href: "#",
className: "undecorated-link debug-target-pane__title " +
"js-debug-target-pane-title",
onClick: e => this.toggleCollapsibility(),
},
dom.h2(

Просмотреть файл

@ -11,7 +11,7 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const FluentReact = require("devtools/client/shared/vendor/fluent-react");
const Localized = createFactory(FluentReact.Localized);
const { PAGES, RUNTIMES } = require("../../constants");
const { PAGE_TYPES, RUNTIMES } = require("../../constants");
const Types = require("../../types/index");
loader.lazyRequireGetter(this, "ADB_ADDON_STATES", "devtools/shared/adb/adb-addon", true);
@ -33,6 +33,7 @@ class Sidebar extends PureComponent {
isScanningUsb: PropTypes.bool.isRequired,
networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
selectedPage: PropTypes.string,
selectedRuntime: PropTypes.string,
usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
};
}
@ -57,7 +58,6 @@ class Sidebar extends PureComponent {
return SidebarItem(
{
isSelected: false,
selectable: false,
},
Localized(
{
@ -88,20 +88,21 @@ class Sidebar extends PureComponent {
}
renderSidebarItems(icon, runtimes) {
const { dispatch, selectedPage } = this.props;
const { dispatch, selectedPage, selectedRuntime } = this.props;
return runtimes.map(runtime => {
const pageId = runtime.type + "-" + runtime.id;
const keyId = `${runtime.type}-${runtime.id}`;
const runtimeHasDetails = !!runtime.runtimeDetails;
const isSelected = selectedPage === PAGE_TYPES.RUNTIME &&
runtime.id === selectedRuntime;
return SidebarRuntimeItem({
id: pageId,
deviceName: runtime.extra.deviceName,
dispatch,
icon,
key: keyId,
isConnected: runtimeHasDetails,
isSelected: selectedPage === pageId,
key: pageId,
isSelected,
name: runtime.name,
runtimeId: runtime.id,
});
@ -109,7 +110,7 @@ class Sidebar extends PureComponent {
}
render() {
const { dispatch, selectedPage, isScanningUsb } = this.props;
const { dispatch, selectedPage, selectedRuntime, isScanningUsb } = this.props;
return dom.aside(
{
@ -120,28 +121,29 @@ class Sidebar extends PureComponent {
Localized(
{ id: "about-debugging-sidebar-this-firefox", attrs: { name: true } },
SidebarFixedItem({
id: PAGES.THIS_FIREFOX,
dispatch,
icon: FIREFOX_ICON,
isSelected: PAGES.THIS_FIREFOX === selectedPage,
isSelected: PAGE_TYPES.RUNTIME === selectedPage &&
selectedRuntime === RUNTIMES.THIS_FIREFOX,
key: RUNTIMES.THIS_FIREFOX,
name: "This Firefox",
runtimeId: RUNTIMES.THIS_FIREFOX,
to: `/runtime/${RUNTIMES.THIS_FIREFOX}`,
})
),
Localized(
{ id: "about-debugging-sidebar-connect", attrs: { name: true } },
SidebarFixedItem({
id: PAGES.CONNECT,
dispatch,
icon: CONNECT_ICON,
isSelected: PAGES.CONNECT === selectedPage,
isSelected: PAGE_TYPES.CONNECT === selectedPage,
key: PAGE_TYPES.CONNECT,
name: "Connect",
to: "/connect",
})
),
SidebarItem(
{
isSelected: false,
selectable: false,
key: "separator-0",
},
dom.hr({ className: "separator" }),
this.renderAdbAddonStatus(),
@ -151,7 +153,7 @@ class Sidebar extends PureComponent {
{
className: "sidebar-item--breathe sidebar__refresh-usb",
isSelected: false,
selectable: false,
key: "refresh-devices",
},
RefreshDevicesButton({
dispatch,

Просмотреть файл

@ -10,40 +10,31 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const SidebarItem = createFactory(require("./SidebarItem"));
const Actions = require("../../actions/index");
/**
* This component displays a fixed item in the Sidebar component.
*/
class SidebarFixedItem extends PureComponent {
static get propTypes() {
return {
dispatch: PropTypes.func.isRequired,
icon: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
isSelected: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
runtimeId: PropTypes.string,
to: PropTypes.string,
};
}
render() {
const {
dispatch,
id,
icon,
isSelected,
name,
runtimeId,
to,
} = this.props;
return SidebarItem(
{
isSelected,
selectable: true,
onSelect: () => {
dispatch(Actions.selectPage(id, runtimeId));
},
to,
},
dom.div(
{

Просмотреть файл

@ -4,9 +4,10 @@
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Link = createFactory(require("devtools/client/shared/vendor/react-router-dom").Link);
/**
* This component is used as a wrapper by items in the sidebar.
@ -17,34 +18,18 @@ class SidebarItem extends PureComponent {
children: PropTypes.node.isRequired,
className: PropTypes.string,
isSelected: PropTypes.bool.isRequired,
selectable: PropTypes.bool.isRequired,
// only require `onSelect` function when `selectable` is true
onSelect: (props, propName, componentName) => {
const isFn = props[propName] && typeof props[propName] === "function";
if (props.selectable && !isFn) {
return new Error(`Missing ${propName} function supplied to ${componentName}. ` +
"(you must set this prop when selectable is true)");
}
return null; // for eslint (consistent-return rule)
},
to: PropTypes.string,
};
}
// temporary handler until a router is in place
onItemClick(evt) {
evt.preventDefault();
this.props.onSelect();
}
renderContent() {
const { children, selectable } = this.props;
const { children, to } = this.props;
if (selectable) {
return dom.a(
if (to) {
return Link(
{
className: "sidebar-item__link js-sidebar-link",
href: "#", // to be changed with a path when a router is in place
onClick: (evt) => this.onItemClick(evt),
to,
},
children
);
@ -54,7 +39,7 @@ class SidebarItem extends PureComponent {
}
render() {
const {className, isSelected, selectable } = this.props;
const {className, isSelected, to } = this.props;
return dom.li(
{
@ -64,7 +49,7 @@ class SidebarItem extends PureComponent {
" sidebar-item--selected js-sidebar-item-selected" :
""
) +
(selectable ? " sidebar-item--selectable" : ""),
(to ? " sidebar-item--selectable" : ""),
},
this.renderContent()
);

Просмотреть файл

@ -20,7 +20,6 @@ const Actions = require("../../actions/index");
class SidebarRuntimeItem extends PureComponent {
static get propTypes() {
return {
id: PropTypes.string.isRequired,
deviceName: PropTypes.string,
dispatch: PropTypes.func.isRequired,
// Provided by wrapping the component with FluentReact.withLocalization.
@ -76,10 +75,8 @@ class SidebarRuntimeItem extends PureComponent {
render() {
const {
deviceName,
dispatch,
getString,
icon,
id,
isConnected,
isSelected,
name,
@ -93,10 +90,7 @@ class SidebarRuntimeItem extends PureComponent {
return SidebarItem(
{
isSelected,
selectable: isConnected,
onSelect: () => {
dispatch(Actions.selectPage(id, runtimeId));
},
to: isConnected ? `/runtime/${encodeURIComponent(runtimeId)}` : null,
},
dom.div(
{

Просмотреть файл

@ -59,8 +59,8 @@ const DEBUG_TARGET_PANE = {
TEMPORARY_EXTENSION: "temporaryExtension",
};
const PAGES = {
THIS_FIREFOX: "this-firefox",
const PAGE_TYPES = {
RUNTIME: "runtime",
CONNECT: "connect",
};
@ -104,7 +104,7 @@ const USB_STATES = {
module.exports = Object.assign({}, {
DEBUG_TARGETS,
DEBUG_TARGET_PANE,
PAGES,
PAGE_TYPES,
PREFERENCES,
RUNTIME_PREFERENCE,
RUNTIMES,

Просмотреть файл

@ -8,6 +8,8 @@ const { ADB } = require("devtools/shared/adb/adb");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const { DebuggerServer } = require("devtools/server/main");
const { ClientWrapper } = require("./client-wrapper");
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
const { RUNTIMES } = require("../constants");
@ -16,15 +18,14 @@ async function createLocalClient() {
DebuggerServer.registerAllActors();
const client = new DebuggerClient(DebuggerServer.connectPipe());
await client.connect();
return { clientWrapper: new ClientWrapper(client) };
return new ClientWrapper(client);
}
async function createNetworkClient(host, port) {
const transportDetails = { host, port };
const transport = await DebuggerClient.socketConnect(transportDetails);
const transport = await DebuggerClient.socketConnect({ host, port });
const client = new DebuggerClient(transport);
await client.connect();
return { clientWrapper: new ClientWrapper(client), transportDetails };
return new ClientWrapper(client);
}
async function createUSBClient(socketPath) {
@ -33,10 +34,13 @@ async function createUSBClient(socketPath) {
}
async function createClientForRuntime(runtime) {
const { extra, type } = runtime;
const { extra, id, type } = runtime;
if (type === RUNTIMES.THIS_FIREFOX) {
return createLocalClient();
} else if (remoteClientManager.hasClient(id, type)) {
const client = remoteClientManager.getClient(id, type);
return new ClientWrapper(client);
} else if (type === RUNTIMES.NETWORK) {
const { host, port } = extra.connectionParameters;
return createNetworkClient(host, port);

Просмотреть файл

@ -19,6 +19,9 @@ const {
findRuntimeById,
} = require("../modules/runtimes-state-helper");
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
// Map between known runtime types and nodes in the runtimes state.
const TYPE_TO_RUNTIMES_KEY = {
[RUNTIMES.THIS_FIREFOX]: "thisFirefoxRuntimes",
@ -70,12 +73,14 @@ function _updateRuntimeById(runtimeId, updatedRuntime, state) {
function runtimesReducer(state = RuntimesState(), action) {
switch (action.type) {
case CONNECT_RUNTIME_SUCCESS: {
const { id, runtimeDetails } = action.runtime;
const { id, runtimeDetails, type } = action.runtime;
remoteClientManager.setClient(id, type, runtimeDetails.clientWrapper.client);
return _updateRuntimeById(id, { runtimeDetails }, state);
}
case DISCONNECT_RUNTIME_SUCCESS: {
const { id } = action.runtime;
const { id, type } = action.runtime;
remoteClientManager.removeClient(id, type);
return _updateRuntimeById(id, { runtimeDetails: null }, state);
}
@ -112,8 +117,8 @@ function runtimesReducer(state = RuntimesState(), action) {
const { runtimes } = action;
const usbRuntimes = runtimes.map(runtime => {
const existingRuntime = findRuntimeById(runtime.id, state);
const existingRuntimeDetails =
existingRuntime ? existingRuntime.runtimeDetails : null;
const existingRuntimeDetails = existingRuntime ?
existingRuntime.runtimeDetails : null;
return {
id: runtime.id,

Просмотреть файл

@ -22,6 +22,7 @@ function UiState(locations = [], debugTargetCollapsibilities = {},
networkEnabled,
networkLocations: locations,
selectedPage: null,
selectedRuntime: null,
showSystemAddons,
wifiEnabled,
};
@ -47,8 +48,9 @@ function uiReducer(state = UiState(), action) {
}
case PAGE_SELECTED: {
const { page } = action;
return Object.assign({}, state, { selectedPage: page });
const { page, runtimeId } = action;
return Object.assign({}, state,
{ selectedPage: page, selectedRuntime: runtimeId });
}
case USB_RUNTIMES_SCAN_START: {

Просмотреть файл

@ -22,14 +22,6 @@ const runtimeInfo = {
version: PropTypes.string.isRequired,
};
const runtimeTransportDetails = {
// host name of tcp connection to debugger server
host: PropTypes.string.isRequired,
// port number of tcp connection to debugger server
port: PropTypes.number.isRequired,
};
const runtimeDetails = {
// ClientWrapper built using a DebuggerClient for the runtime
clientWrapper: PropTypes.instanceOf(ClientWrapper).isRequired,
@ -39,10 +31,6 @@ const runtimeDetails = {
// runtime information
info: PropTypes.shape(runtimeInfo).isRequired,
// tcp connection information,
// unavailable on this-firefox runtime
transportDetails: PropTypes.shape(runtimeTransportDetails),
};
const networkRuntimeConnectionParameter = {

Просмотреть файл

@ -27,6 +27,8 @@ skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug
[browser_aboutdebugging_debug-target-pane_empty.js]
[browser_aboutdebugging_debug-target-pane_usb_runtime.js]
[browser_aboutdebugging_navigate.js]
[browser_aboutdebugging_persist_connection.js]
[browser_aboutdebugging_routes.js]
[browser_aboutdebugging_sidebar_network_runtimes.js]
[browser_aboutdebugging_sidebar_usb_runtime.js]
[browser_aboutdebugging_sidebar_usb_runtime_connect.js]

Просмотреть файл

@ -24,7 +24,8 @@ add_task(async function() {
});
usbMocks.emitUpdate();
await connectToRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
await connectToRuntime(RUNTIME_DEVICE_NAME, document);
await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
const extensionPane = getDebugTargetPane("Extensions", document);
info("Check an empty target pane message is displayed");

Просмотреть файл

@ -13,31 +13,39 @@ add_task(async function() {
const { document, tab } = await openAboutDebugging();
for (const { title } of TARGET_PANES) {
const debugTargetPaneEl = getDebugTargetPane(title, document);
info("Check whether this pane is collapsed after clicking the title");
await toggleCollapsibility(debugTargetPaneEl);
assertCollapsibility(debugTargetPaneEl, title, true);
await toggleCollapsibility(getDebugTargetPane(title, document));
assertDebugTargetCollapsed(getDebugTargetPane(title, document), title);
info("Check whether this pane is expanded after clicking the title again");
await toggleCollapsibility(debugTargetPaneEl);
assertCollapsibility(debugTargetPaneEl, title, false);
await toggleCollapsibility(getDebugTargetPane(title, document));
await assertDebugTargetExpanded(getDebugTargetPane(title, document), title);
}
await removeTab(tab);
});
function assertCollapsibility(debugTargetPaneEl, title, shouldCollapsed) {
info("Check height of list");
const listEl = debugTargetPaneEl.querySelector(".js-debug-target-list");
const assertHeight = shouldCollapsed ? is : isnot;
assertHeight(listEl.clientHeight, 0, "Height of list element should correct");
async function assertDebugTargetCollapsed(paneEl, title) {
info("Check debug target is collapsed");
info("Check content of title");
const titleEl = debugTargetPaneEl.querySelector(".js-debug-target-pane-title");
// check list height
const listEl = paneEl.querySelector(".js-debug-target-list");
is(listEl.clientHeight, 0, "Height of list element is zero");
// check title
const titleEl = paneEl.querySelector(".js-debug-target-pane-title");
const expectedTitle =
shouldCollapsed
? `${ title } (${ listEl.querySelectorAll(".js-debug-target-item").length })`
: title;
is(titleEl.textContent, expectedTitle, "Title should correct");
`${ title } (${ listEl.querySelectorAll(".js-debug-target-item").length })`;
is(titleEl.textContent, expectedTitle, "Collapsed title is correct");
}
async function assertDebugTargetExpanded(paneEl, title) {
info("Check debug target is expanded");
// check list height
const listEl = paneEl.querySelector(".js-debug-target-list");
await waitUntil(() => listEl.clientHeight > 0);
ok(true, "Height of list element is greater than zero");
// check title
const titleEl = paneEl.querySelector(".js-debug-target-pane-title");
is(titleEl.textContent, title, "Expanded title is correct");
}

Просмотреть файл

@ -26,7 +26,8 @@ add_task(async function() {
});
usbMocks.emitUpdate();
await connectToRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
await connectToRuntime(RUNTIME_DEVICE_NAME, document);
await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
const SUPPORTED_TARGET_PANES = [
"Extensions",

Просмотреть файл

@ -40,7 +40,9 @@ add_task(async function() {
info("Wait until Connect page is displayed");
await waitUntil(() => document.querySelector(".js-connect-page"));
ok(isSidebarItemSelected(connectSidebarItem), "Connect sidebar item is selected");
// we need to wait here because the sidebar isn't updated after mounting the page
info("Wait until Connect sidebar item is selected");
await waitUntil(() => isSidebarItemSelected(connectSidebarItem));
ok(!document.querySelector(".js-runtime-page"), "Runtime page no longer rendered");
info("Open a new tab which should be listed when we go back to This Firefox");

Просмотреть файл

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const RUNTIME_ID = "test-runtime-id";
const RUNTIME_DEVICE_NAME = "test device name";
const RUNTIME_APP_NAME = "TestApp";
/* import-globals-from mocks/head-usb-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
// Test that remote runtime connections are persisted across about:debugging reloads.
add_task(async function() {
const usbMocks = new UsbMocks();
usbMocks.enableMocks();
registerCleanupFunction(() => usbMocks.disableMocks());
let { document, tab } = await openAboutDebugging();
const usbClient = usbMocks.createRuntime(RUNTIME_ID, {
name: RUNTIME_APP_NAME,
deviceName: RUNTIME_DEVICE_NAME,
});
usbMocks.emitUpdate();
await connectToRuntime(RUNTIME_DEVICE_NAME, document);
await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
info("Reload about:debugging");
document = await reloadAboutDebugging(tab);
usbMocks.emitUpdate();
info("Wait until the remote runtime appears as connected");
await waitUntil(() => {
const sidebarItem = findSidebarItemByText(RUNTIME_DEVICE_NAME, document);
return sidebarItem && !sidebarItem.querySelector(".js-connect-button");
});
// Remove the runtime without emitting an update.
// This is what happens today when we simply close Firefox for Android.
info("Remove the runtime from the list of USB runtimes");
usbMocks.removeRuntime(RUNTIME_ID);
info("Emit 'closed' on the client and wait for the sidebar item to disappear");
usbClient._eventEmitter.emit("closed");
await waitUntil(() => !findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
info("Remove the tab");
await removeTab(tab);
});

Просмотреть файл

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from mocks/head-usb-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
/**
* Test that the initial route is /runtime/this-firefox
*/
add_task(async function() {
info("Check root route redirects to 'This Firefox'");
const { document, tab } = await openAboutDebugging();
is(document.location.hash, "#/runtime/this-firefox");
await removeTab(tab);
});
/**
* Test that the routes in about:debugging show the proper views
*/
add_task(async function() {
// enable USB devices mocks
const usbMocks = new UsbMocks();
usbMocks.enableMocks();
registerCleanupFunction(() => usbMocks.disableMocks());
const { document, tab } = await openAboutDebugging();
info("Check 'This Firefox' route");
document.location.hash = "#/runtime/this-firefox";
await waitUntil(() => document.querySelector(".js-runtime-page"));
const infoLabel = document.querySelector(".js-runtime-info").textContent;
// NOTE: when using USB Mocks, we see only "Firefox" as the device name
ok(infoLabel.includes("Firefox"), "Runtime is displayed as Firefox");
ok(!infoLabel.includes(" on "), "Runtime is not associated to any device");
info("Check 'Connect' page");
document.location.hash = "#/connect";
await waitUntil(() => document.querySelector(".js-connect-page"));
ok(true, "Connect page has been shown");
info("Check 'USB device runtime' page");
// connect to a mocked USB runtime
usbMocks.createRuntime("1337id", {
deviceName: "Fancy Phone",
name: "Lorem ipsum",
});
usbMocks.emitUpdate();
await connectToRuntime("Fancy Phone", document);
// navigate to it via URL
document.location.hash = "#/runtime/1337id";
await waitUntil(() => document.querySelector(".js-runtime-page"));
const runtimeLabel = document.querySelector(".js-runtime-info").textContent;
ok(runtimeLabel.includes("Lorem ipsum"), "Runtime is displayed with the mocked name");
await removeTab(tab);
});
/**
* Test that an invalid route redirects to / (currently This Firefox page)
*/
add_task(async function() {
info("Check an invalid route redirects to root");
const { document, tab } = await openAboutDebugging();
info("Waiting for a non-runtime page to load");
document.location.hash = "#/connect";
await waitUntil(() => document.querySelector(".js-connect-page"));
info("Update hash & wait for a redirect to root ('This Firefox')");
document.location.hash = "#/lorem-ipsum";
await waitUntil(() => document.querySelector(".js-runtime-page"));
is(document.location.hash, "#/runtime/this-firefox", "Redirected to root");
await removeTab(tab);
});

Просмотреть файл

@ -28,7 +28,8 @@ add_task(async function() {
});
usbMocks.emitUpdate();
await connectToRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
await connectToRuntime(RUNTIME_DEVICE_NAME, document);
await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
info("Create a second runtime and click on Refresh Devices");
usbMocks.createRuntime(OTHER_RUNTIME_ID, {

Просмотреть файл

@ -59,7 +59,7 @@ function setupThisFirefoxMock() {
runtimeClientFactoryMock.createClientForRuntime = runtime => {
const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
if (runtime.id === RUNTIMES.THIS_FIREFOX) {
return { clientWrapper: thisFirefoxClient };
return thisFirefoxClient;
}
throw new Error("Unexpected runtime id " + runtime.id);
};

Просмотреть файл

@ -23,7 +23,7 @@ add_task(async function() {
runtimeClientFactoryMock.createClientForRuntime = runtime => {
const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
if (runtime.id === RUNTIMES.THIS_FIREFOX) {
return { clientWrapper: thisFirefoxClient };
return thisFirefoxClient;
}
throw new Error("Unexpected runtime id " + runtime.id);
};

Просмотреть файл

@ -33,8 +33,14 @@ const TARGET_PANES = [
// eslint-disable-next-line no-unused-vars
function getDebugTargetPane(title, document) {
// removes the suffix "(<NUMBER>)" in debug target pane's title, if needed
const sanitizeTitle = (x) => {
return x.replace(/\s+\(\d+\)$/, "");
};
const targetTitle = sanitizeTitle(title);
for (const titleEl of document.querySelectorAll(".js-debug-target-pane-title")) {
if (titleEl.textContent !== title) {
if (sanitizeTitle(titleEl.textContent) !== targetTitle) {
continue;
}

Просмотреть файл

@ -32,6 +32,10 @@ registerCleanupFunction(async function() {
}
const { ADB } = require("devtools/shared/adb/adb");
await ADB.kill();
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
await remoteClientManager.removeAllClients();
});
/**
@ -46,19 +50,37 @@ async function openAboutDebugging(page, win) {
await enableNewAboutDebugging();
info("opening about:debugging");
const tab = await addTab("about:debugging", { window: win });
const browser = tab.linkedBrowser;
const document = browser.contentDocument;
const window = browser.contentWindow;
const { AboutDebugging } = window;
await waitForInitialDispatch(window);
await Promise.all([
return { tab, document, window };
}
async function reloadAboutDebugging(tab) {
info("reload about:debugging");
await refreshTab(tab);
const browser = tab.linkedBrowser;
const document = browser.contentDocument;
const window = browser.contentWindow;
await waitForInitialDispatch(window);
return document;
}
function waitForInitialDispatch(win) {
info("wait for the initial about debugging actions to be dispatched");
const { AboutDebugging } = win;
return Promise.all([
waitForDispatch(AboutDebugging.store, "REQUEST_EXTENSIONS_SUCCESS"),
waitForDispatch(AboutDebugging.store, "REQUEST_TABS_SUCCESS"),
waitForDispatch(AboutDebugging.store, "REQUEST_WORKERS_SUCCESS"),
]);
return { tab, document, window };
}
/**
@ -140,7 +162,7 @@ function findSidebarItemByText(text, document) {
});
}
async function connectToRuntime(deviceName, appName, document) {
async function connectToRuntime(deviceName, document) {
info(`Wait until the sidebar item for ${deviceName} appears`);
await waitUntil(() => findSidebarItemByText(deviceName, document));
const sidebarItem = findSidebarItemByText(deviceName, document);
@ -150,11 +172,14 @@ async function connectToRuntime(deviceName, appName, document) {
info("Click on the connect button and wait until it disappears");
connectButton.click();
await waitUntil(() => !sidebarItem.querySelector(".js-connect-button"));
}
async function selectRuntime(deviceName, name, document) {
const sidebarItem = findSidebarItemByText(deviceName, document);
sidebarItem.querySelector(".js-sidebar-link").click();
await waitUntil(() => {
const runtimeInfo = document.querySelector(".js-runtime-info");
return runtimeInfo.textContent.includes(appName);
return runtimeInfo.textContent.includes(name);
});
}

Просмотреть файл

@ -35,6 +35,19 @@ function createClientMock() {
removeListener: (evt, listener) => {
eventEmitter.off(evt, listener);
},
client: {
addOneTimeListener: (evt, listener) => {
eventEmitter.once(evt, listener);
},
addListener: (evt, listener) => {
eventEmitter.on(evt, listener);
},
removeListener: (evt, listener) => {
eventEmitter.off(evt, listener);
},
},
// no-op
close: () => {},
// no-op

Просмотреть файл

@ -28,6 +28,12 @@ class UsbMocks {
return this._runtimes;
};
// refreshUSBRuntimes normally starts scan, which should ultimately fire the
// "runtime-list-updated" event.
this.usbRuntimesMock.refreshUSBRuntimes = () => {
this.emitUpdate();
};
// Prepare a fake observer to be able to emit events from this mock.
this._observerMock = addObserverMock(this.usbRuntimesMock);
@ -35,7 +41,7 @@ class UsbMocks {
this.runtimeClientFactoryMock = createRuntimeClientFactoryMock();
this._clients = {};
this.runtimeClientFactoryMock.createClientForRuntime = runtime => {
return { clientWrapper: this._clients[runtime.id] };
return this._clients[runtime.id];
};
// Add a client for THIS_FIREFOX, since about:debugging will start on the This Firefox

Просмотреть файл

@ -7,6 +7,8 @@
const { TargetFactory } = require("devtools/client/framework/target");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const { remoteClientManager } =
require("devtools/client/shared/remote-debugging/remote-client-manager");
/**
* Construct a Target for a given URL object having various query parameters:
@ -39,9 +41,14 @@ const { DebuggerClient } = require("devtools/shared/client/debugger-client");
*/
exports.targetFromURL = async function targetFromURL(url) {
const client = await clientFromURL(url);
await client.connect();
const params = url.searchParams;
// Clients retrieved from the remote-client-manager are already connected.
if (!params.get("remoteId")) {
// Connect any other client.
await client.connect();
}
const type = params.get("type");
if (!type) {
throw new Error("targetFromURL, missing type parameter");
@ -116,6 +123,8 @@ exports.targetFromURL = async function targetFromURL(url) {
* {String} The hostname or IP address to connect to.
* port:
* {Number} The TCP port to connect to, to use with `host` argument.
* remoteId:
* {String} Remote client id, for runtimes from the remote-client-manager
* ws:
* {Boolean} If true, connect via websocket instead of regular TCP connection.
*
@ -125,6 +134,13 @@ exports.targetFromURL = async function targetFromURL(url) {
*/
async function clientFromURL(url) {
const params = url.searchParams;
// If a remote id was provided we should already have a connected client available.
const remoteId = params.get("remoteId");
if (remoteId) {
return remoteClientManager.getClientByRemoteId(remoteId);
}
const host = params.get("host");
const port = params.get("port");
const webSocket = !!params.get("ws");

Просмотреть файл

@ -13,6 +13,7 @@ TEST_HARNESS_FILES.xpcshell.devtools.client.shared.test += [
DIRS += [
'components',
'redux',
'remote-debugging',
'source-map',
'vendor',
'webpack',

Просмотреть файл

@ -1,8 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'Devices.jsm'
'remote-client-manager.js',
)
with Files('**'):
BUG_COMPONENT = ('DevTools', 'about:debugging')

Просмотреть файл

@ -0,0 +1,98 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* This class is designed to be a singleton shared by all DevTools to get access to
* existing clients created for remote debugging.
*/
class RemoteClientManager {
constructor() {
this._clients = new Map();
this._onClientClosed = this._onClientClosed.bind(this);
}
/**
* Store a remote client that is already connected.
*
* @param {String} id
* Remote runtime id (see devtools/client/aboutdebugging-new/src/types).
* @param {String} type
* Remote runtime type (see devtools/client/aboutdebugging-new/src/types).
* @param {DebuggerClient} client
*/
setClient(id, type, client) {
const key = this._getKey(id, type);
this._clients.set(key, client);
client.addOneTimeListener("closed", this._onClientClosed);
}
// See JSDoc for id, type from setClient.
hasClient(id, type) {
return this._clients.has(this._getKey(id, type));
}
// See JSDoc for id, type from setClient.
getClient(id, type) {
return this._clients.get(this._getKey(id, type));
}
// See JSDoc for id, type from setClient.
removeClient(id, type) {
const key = this._getKey(id, type);
this._removeClientByKey(key);
}
removeAllClients() {
const keys = [...this._clients.keys()];
for (const key of keys) {
this._removeClientByKey(key);
}
}
/**
* Retrieve a unique, url-safe key based on a runtime id and type.
*/
getRemoteId(id, type) {
return encodeURIComponent(this._getKey(id, type));
}
/**
* Retrieve a managed client for a remote id. The remote id should have been generated
* using getRemoteId.
*/
getClientByRemoteId(remoteId) {
const key = decodeURIComponent(remoteId);
return this._clients.get(key);
}
_getKey(id, type) {
return id + "-" + type;
}
_removeClientByKey(key) {
const client = this._clients.get(key);
if (client) {
client.removeListener("closed", this._onClientClosed);
this._clients.delete(key);
}
}
/**
* Cleanup all closed clients when a "closed" notification is received from a client.
*/
_onClientClosed() {
const closedClientKeys = [...this._clients.keys()].filter(key => {
return this._clients.get(key)._closed;
});
for (const key of closedClientKeys) {
this._removeClientByKey(key);
}
}
}
// Expose a singleton of RemoteClientManager.
exports.remoteClientManager = new RemoteClientManager();

23
devtools/client/shared/vendor/REACT_ROUTER_DOM_UPGRADING.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
[//]: # (
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
)
Follow this steps to upgrade the `react-router-dom` library:
1. Clone the repository (note: this is a monorepository, it includes other libraries too):
`git clone git@github.com:ReactTraining/react-router.git`
2. Install build dependencies:
`cd react-router`
`npm install`
3. Run a build :
`npm run build`
4. Grab the UMD build of `react-router-dom`, which is located in `packages/react-router-dom/umd/react-router-dom.js` and copy it into Firefox source tree.
5. Edit `react-router-dom.js` and change `require('react')` for `require('devtools/client/shared/vendor/react')`
6. Update the version below:
The current version is 4.3.1

1
devtools/client/shared/vendor/moz.build поставляемый
Просмотреть файл

@ -21,6 +21,7 @@ DevToolsModules(
'react-dom.js',
'react-prop-types.js',
'react-redux.js',
'react-router-dom.js',
'react-test-renderer-shallow.js',
'react.js',
'redux.js',

3788
devtools/client/shared/vendor/react-router-dom.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -8,7 +8,8 @@ const Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const EventEmitter = require("devtools/shared/event-emitter");
const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes");
const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
const { adbAddon, ADB_ADDON_STATES } = require("devtools/shared/adb/adb-addon");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
var RuntimeList;
@ -141,7 +142,7 @@ RuntimeList.prototype = {
noADBExtensionNode.textContent =
Strings.formatStringFromName("runtimePanel_noadbextension", ["ADB Extension"], 1);
if (Devices.adbExtensionInstalled) {
if (adbAddon.status === ADB_ADDON_STATES.INSTALLED) {
noADBExtensionNode.setAttribute("hidden", "true");
} else {
noADBExtensionNode.removeAttribute("hidden");
@ -153,7 +154,7 @@ RuntimeList.prototype = {
return;
}
if (runtimeList.usb.length === 0 && Devices.adbExtensionInstalled) {
if (runtimeList.usb.length === 0 && adbAddon.status === ADB_ADDON_STATES.INSTALLED) {
noUSBNode.removeAttribute("hidden");
} else {
noUSBNode.setAttribute("hidden", "true");

Просмотреть файл

@ -21,15 +21,18 @@
window.onload = function() {
SimpleTest.waitForExplicitFinish();
const {Devices} = ChromeUtils.import("resource://devtools/shared/apps/Devices.jsm", {});
const { adbAddon, ADB_ADDON_STATES } = require("devtools/shared/adb/adb-addon");
function isAdbAddonInstalled() {
return adbAddon.status === ADB_ADDON_STATES.INSTALLED;
}
function uninstallADBFromUI(doc) {
return new Promise((resolve, reject) => {
Devices.on("addon-status-updated", function onUpdate() {
adbAddon.on("update", function onUpdate() {
nextTick().then(() => {
const li = doc.querySelector('[status="uninstalled"][addon="adb"]');
if (li) {
Devices.off("addon-status-updated", onUpdate);
adbAddon.off("update", onUpdate);
resolve();
} else {
reject("Can't find item");
@ -42,16 +45,16 @@
}
(async function() {
ok(!Devices.adbExtensionInstalled, "ADB extension not installed");
ok(!isAdbAddonInstalled(), "ADB extension not installed");
const win = await openWebIDE(true);
// ADB is installed asynchronously after starting WebIDE.
while (!Devices.adbExtensionInstalled) {
await Devices.once("addon-status-updated");
while (!isAdbAddonInstalled()) {
await adbAddon.once("update");
}
ok(Devices.adbExtensionInstalled, "ADB extension has been auto-installed");
ok(isAdbAddonInstalled(), "ADB extension has been auto-installed");
await nextTick();

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -10,24 +10,44 @@ const { ADB } = require("devtools/shared/adb/adb");
* A Device instance is created and registered with the Devices module whenever
* ADB notices a new device is connected.
*/
function Device(id) {
this.id = id;
class AdbDevice {
constructor(id) {
this.id = id;
}
async getModel() {
if (this._model) {
return this._model;
}
const model = await ADB.shell("getprop ro.product.model");
this._model = model.trim();
return this._model;
}
// This method is not using any information from the instance, but in theory getting
// runtime socket paths (as well as model) should be device specific. So we should use
// information available on the instance when implementing multi device support.
// See Bug 1507126.
async getRuntimeSocketPaths() {
// A matching entry looks like:
// 00000000: 00000002 00000000 00010000 0001 01 6551588
// /data/data/org.mozilla.fennec/firefox-debugger-socket
const query = "cat /proc/net/unix";
const rawSocketInfo = await ADB.shell(query);
// Filter to lines with "firefox-debugger-socket"
let socketInfos = rawSocketInfo.split(/\r?\n/);
socketInfos = socketInfos.filter(l => l.includes("firefox-debugger-socket"));
// It's possible to have multiple lines with the same path, so de-dupe them
const socketPaths = new Set();
for (const socketInfo of socketInfos) {
const socketPath = socketInfo.split(" ").pop();
socketPaths.add(socketPath);
}
return socketPaths;
}
}
Device.prototype = {
type: "adb",
shell: ADB.shell.bind(ADB),
getModel() {
if (this._modelPromise) {
return this._modelPromise;
}
this._modelPromise = this.shell("getprop ro.product.model")
.then(model => model.trim());
return this._modelPromise;
},
// push, pull were removed in Bug 1481691.
};
module.exports = Device;
module.exports = AdbDevice;

Просмотреть файл

@ -0,0 +1,80 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const { adbAddon, ADB_ADDON_STATES } = require("devtools/shared/adb/adb-addon");
/**
* Shared registry that will hold all the detected devices from ADB.
* Extends EventEmitter and emits the following events:
* - "register": a new device has been registered
* - "unregister": a device has been unregistered
*/
class AdbDevicesRegistry extends EventEmitter {
constructor() {
super();
// Internal object to store the discovered adb devices.
this._devices = {};
// When the addon is uninstalled, the repository should be emptied.
// TODO: This should also be done when ADB is stopped.
this._onAdbAddonUpdate = this._onAdbAddonUpdate.bind(this);
adbAddon.on("update", this._onAdbAddonUpdate);
}
/**
* Register a device (Device class defined in from adb-device.js) for the provided name.
*
* @param {String} name
* Name of the device.
* @param {AdbDevice} device
* The device to register.
*/
register(name, device) {
this._devices[name] = device;
this.emit("register");
}
/**
* Unregister a device previously registered under the provided name.
*
* @param {String} name
* Name of the device.
*/
unregister(name) {
delete this._devices[name];
this.emit("unregister");
}
/**
* Returns an iterable containing the name of all the available devices, sorted by name.
*/
available() {
return Object.keys(this._devices).sort();
}
/**
* Returns a device previously registered under the provided name.
*
* @param {String} name
* Name of the device.
*/
getByName(name) {
return this._devices[name];
}
_onAdbAddonUpdate() {
const installed = adbAddon.status === ADB_ADDON_STATES.INSTALLED;
if (!installed) {
for (const name in this._devices) {
this.unregister(name);
}
}
}
}
exports.adbDevicesRegistry = new AdbDevicesRegistry();

Просмотреть файл

@ -0,0 +1,73 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { RuntimeTypes } = require("devtools/client/webide/modules/runtime-types");
const { ADB } = require("devtools/shared/adb/adb");
class AdbRuntime {
constructor(adbDevice, model, socketPath) {
this.type = RuntimeTypes.USB;
this._adbDevice = adbDevice;
this._model = model;
this._socketPath = socketPath;
}
get id() {
return this._adbDevice.id + "|" + this._socketPath;
}
get deviceName() {
return this._model || this._adbDevice.id;
}
get shortName() {
return `Firefox ${this._channel()}`;
}
get name() {
return `Firefox ${this._channel()} on Android (${this.deviceName})`;
}
connect(connection) {
return ADB.prepareTCPConnection(this._socketPath).then(port => {
connection.host = "localhost";
connection.port = port;
connection.connect();
});
}
_channel() {
const packageName = this._packageName();
switch (packageName) {
case "org.mozilla.firefox":
return "";
case "org.mozilla.firefox_beta":
return "Beta";
case "org.mozilla.fennec":
case "org.mozilla.fennec_aurora":
// This package name is now the one for Firefox Nightly distributed
// through the Google Play Store since "dawn project"
// cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1357351#c8
return "Nightly";
default:
return "Custom";
}
}
_packageName() {
// If using abstract socket address, it is "@org.mozilla.firefox/..."
// If using path base socket, it is "/data/data/<package>...""
// Until Fennec 62 only supports path based UNIX domain socket, but
// Fennec 63+ supports both path based and abstract socket.
return this._socketPath.startsWith("@") ?
this._socketPath.substr(1).split("/")[0] :
this._socketPath.split("/")[3];
}
}
exports.AdbRuntime = AdbRuntime;

Просмотреть файл

@ -5,12 +5,12 @@
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const { Devices } = require("devtools/shared/apps/Devices.jsm");
const { dumpn } = require("devtools/shared/DevToolsUtils");
const { RuntimeTypes } =
require("devtools/client/webide/modules/runtime-types");
const { ADB } = require("devtools/shared/adb/adb");
loader.lazyRequireGetter(this, "Device", "devtools/shared/adb/adb-device");
const { adbDevicesRegistry } = require("devtools/shared/adb/adb-devices-registry");
const { AdbRuntime } = require("devtools/shared/adb/adb-runtime");
loader.lazyRequireGetter(this, "AdbDevice", "devtools/shared/adb/adb-device");
class ADBScanner extends EventEmitter {
constructor() {
@ -26,9 +26,8 @@ class ADBScanner extends EventEmitter {
EventEmitter.on(ADB, "device-connected", this._onDeviceConnected);
EventEmitter.on(ADB, "device-disconnected", this._onDeviceDisconnected);
Devices.on("register", this._updateRuntimes);
Devices.on("unregister", this._updateRuntimes);
Devices.on("addon-status-updated", this._updateRuntimes);
adbDevicesRegistry.on("register", this._updateRuntimes);
adbDevicesRegistry.on("unregister", this._updateRuntimes);
ADB.start().then(() => {
ADB.trackDevices();
@ -39,9 +38,9 @@ class ADBScanner extends EventEmitter {
disable() {
EventEmitter.off(ADB, "device-connected", this._onDeviceConnected);
EventEmitter.off(ADB, "device-disconnected", this._onDeviceDisconnected);
Devices.off("register", this._updateRuntimes);
Devices.off("unregister", this._updateRuntimes);
Devices.off("addon-status-updated", this._updateRuntimes);
adbDevicesRegistry.off("register", this._updateRuntimes);
adbDevicesRegistry.off("unregister", this._updateRuntimes);
this._updateRuntimes();
}
_emitUpdated() {
@ -49,12 +48,12 @@ class ADBScanner extends EventEmitter {
}
_onDeviceConnected(deviceId) {
const device = new Device(deviceId);
Devices.register(deviceId, device);
const device = new AdbDevice(deviceId);
adbDevicesRegistry.register(deviceId, device);
}
_onDeviceDisconnected(deviceId) {
Devices.unregister(deviceId);
adbDevicesRegistry.unregister(deviceId);
}
_updateRuntimes() {
@ -63,8 +62,8 @@ class ADBScanner extends EventEmitter {
}
this._runtimes = [];
const promises = [];
for (const id of Devices.available()) {
const device = Devices.getByName(id);
for (const id of adbDevicesRegistry.available()) {
const device = adbDevicesRegistry.getByName(id);
promises.push(this._detectRuntimes(device));
}
this._updatingPromise = Promise.all(promises);
@ -77,11 +76,14 @@ class ADBScanner extends EventEmitter {
return this._updatingPromise;
}
async _detectRuntimes(device) {
const model = await device.getModel();
const detectedRuntimes =
await FirefoxOnAndroidRuntime.detect(device, model);
this._runtimes.push(...detectedRuntimes);
async _detectRuntimes(adbDevice) {
const model = await adbDevice.getModel();
const socketPaths = await adbDevice.getRuntimeSocketPaths();
for (const socketPath of socketPaths) {
const runtime = new AdbRuntime(adbDevice, model, socketPath);
dumpn("Found " + runtime.name);
this._runtimes.push(runtime);
}
}
scan() {
@ -93,103 +95,4 @@ class ADBScanner extends EventEmitter {
}
}
function Runtime(device, model, socketPath) {
this.device = device;
this._model = model;
this._socketPath = socketPath;
}
Runtime.prototype = {
type: RuntimeTypes.USB,
connect(connection) {
return ADB.prepareTCPConnection(this._socketPath).then(port => {
connection.host = "localhost";
connection.port = port;
connection.connect();
});
},
get id() {
return this.device.id + "|" + this._socketPath;
},
};
function FirefoxOnAndroidRuntime(device, model, socketPath) {
Runtime.call(this, device, model, socketPath);
}
// This requires Unix socket support from Firefox for Android (35+)
FirefoxOnAndroidRuntime.detect = async function(device, model) {
const runtimes = [];
// A matching entry looks like:
// 00000000: 00000002 00000000 00010000 0001 01 6551588
// /data/data/org.mozilla.fennec/firefox-debugger-socket
const query = "cat /proc/net/unix";
const rawSocketInfo = await device.shell(query);
let socketInfos = rawSocketInfo.split(/\r?\n/);
// Filter to lines with "firefox-debugger-socket"
socketInfos = socketInfos.filter(l => l.includes("firefox-debugger-socket"));
// It's possible to have multiple lines with the same path, so de-dupe them
const socketPaths = new Set();
for (const socketInfo of socketInfos) {
const socketPath = socketInfo.split(" ").pop();
socketPaths.add(socketPath);
}
for (const socketPath of socketPaths) {
const runtime = new FirefoxOnAndroidRuntime(device, model, socketPath);
dumpn("Found " + runtime.name);
runtimes.push(runtime);
}
return runtimes;
};
FirefoxOnAndroidRuntime.prototype = Object.create(Runtime.prototype);
FirefoxOnAndroidRuntime.prototype._channel = function() {
const packageName = this._packageName();
switch (packageName) {
case "org.mozilla.firefox":
return "";
case "org.mozilla.firefox_beta":
return "Beta";
case "org.mozilla.fennec":
case "org.mozilla.fennec_aurora":
// This package name is now the one for Firefox Nightly distributed
// through the Google Play Store since "dawn project"
// cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1357351#c8
return "Nightly";
default:
return "Custom";
}
};
FirefoxOnAndroidRuntime.prototype._packageName = function() {
// If using abstract socket address, it is "@org.mozilla.firefox/..."
// If using path base socket, it is "/data/data/<package>...""
// Until Fennec 62 only supports path based UNIX domain socket, but
// Fennec 63+ supports both path based and abstract socket.
return this._socketPath.startsWith("@") ?
this._socketPath.substr(1).split("/")[0] :
this._socketPath.split("/")[3];
};
Object.defineProperty(FirefoxOnAndroidRuntime.prototype, "shortName", {
get() {
return `Firefox ${this._channel()}`;
},
});
Object.defineProperty(FirefoxOnAndroidRuntime.prototype, "deviceName", {
get() {
return this._model || this.device.id;
},
});
Object.defineProperty(FirefoxOnAndroidRuntime.prototype, "name", {
get() {
const channel = this._channel();
return "Firefox " + channel + " on Android (" + this.deviceName + ")";
},
});
exports.ADBScanner = ADBScanner;

Просмотреть файл

@ -89,7 +89,6 @@ const ADB = {
const isAdbRunning = await check();
if (isAdbRunning) {
this.didRunInitially = false;
dumpn("Found ADB process running, not restarting");
onSuccessfulStart();
return;

Просмотреть файл

@ -7,7 +7,9 @@ DevToolsModules(
'adb-binary.js',
'adb-client.js',
'adb-device.js',
'adb-devices-registry.js',
'adb-running-checker.js',
'adb-runtime.js',
'adb-scanner.js',
'adb-socket.js',
'adb.js',

Просмотреть файл

@ -175,13 +175,24 @@ add_task({
await extension.startup();
// Call start() once and call stop() afterwards.
await ADB.start();
ok(ADB.ready);
ok(await check(), "adb is now running");
await ADB.stop();
ok(!ADB.ready);
ok(!(await check()), "adb is no longer running");
// Call start() twice and call stop() afterwards.
await ADB.start();
await ADB.start();
ok(ADB.ready);
ok(await check(), "adb is now running");
await ADB.stop();
ok(!ADB.ready);
ok(!(await check()), "adb is no longer running");
await extension.unload();
});

Просмотреть файл

@ -1,64 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const EventEmitter = require("devtools/shared/event-emitter");
const { adbAddon, ADB_ADDON_STATES } = require("devtools/shared/adb/adb-addon");
/* exported EXPORTED_SYMBOLS */
const EXPORTED_SYMBOLS = ["Devices"];
var addonInstalled = adbAddon.status === ADB_ADDON_STATES.INSTALLED;
const Devices = {
_devices: {},
get adbExtensionInstalled() {
return addonInstalled;
},
set adbExtensionInstalled(v) {
addonInstalled = v;
if (!addonInstalled) {
for (const name in this._devices) {
this.unregister(name);
}
}
this.emit("addon-status-updated", v);
},
register: function(name, device) {
this._devices[name] = device;
this.emit("register");
},
unregister: function(name) {
delete this._devices[name];
this.emit("unregister");
},
available: function() {
return Object.keys(this._devices).sort();
},
getByName: function(name) {
return this._devices[name];
},
updateAdbAddonStatus: function() {
this.adbExtensionInstalled = adbAddon.status === ADB_ADDON_STATES.INSTALLED;
},
};
Object.defineProperty(this, "Devices", {
value: Devices,
enumerable: true,
writable: false,
});
EventEmitter.decorate(Devices);
adbAddon.on("update", () => Devices.updateAdbAddonStatus());

Просмотреть файл

@ -27,8 +27,12 @@ function eventSource(proto) {
* Called when the event is fired. If the same listener
* is added more than once, it will be called once per
* addListener call.
* @param key function (optional)
* Key to use for removeListener, defaults to the listener. Used by helper method
* addOneTimeListener, which creates a custom listener. Use the original listener
* as key to allow to remove oneTimeListeners.
*/
proto.addListener = function(name, listener) {
proto.addListener = function(name, listener, key = listener) {
if (typeof listener != "function") {
throw TypeError("Listeners must be functions.");
}
@ -37,7 +41,7 @@ function eventSource(proto) {
this._listeners = {};
}
this._getListeners(name).push(listener);
this._getListeners(name).push({ key, callback: listener });
};
/**
@ -53,14 +57,14 @@ function eventSource(proto) {
*/
proto.addOneTimeListener = function(name, listener) {
return new Promise(resolve => {
const l = (eventName, ...rest) => {
this.removeListener(name, l);
const oneTimeListener = (eventName, ...rest) => {
this.removeListener(name, listener);
if (listener) {
listener(eventName, ...rest);
}
resolve(rest[0]);
};
this.addListener(name, l);
this.addListener(name, oneTimeListener, listener);
});
};
@ -83,7 +87,7 @@ function eventSource(proto) {
this._listeners[name] = [];
} else {
this._listeners[name] =
this._listeners[name].filter(l => l != listener);
this._listeners[name].filter(l => l.key != listener);
}
};
@ -121,7 +125,7 @@ function eventSource(proto) {
for (const listener of listeners) {
try {
listener.apply(null, arguments);
listener.callback.apply(null, arguments);
} catch (e) {
// Prevent a bad listener from interfering with the others.
DevToolsUtils.reportException("notify event '" + name + "'", e);

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше