зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central a=merge
This commit is contained in:
Коммит
8801452ad1
|
@ -799,21 +799,21 @@ name = "encoding_c"
|
|||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"encoding_rs 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_glue"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"encoding_rs 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nserror 0.1.0",
|
||||
"nsstring 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.12"
|
||||
version = "0.8.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1704,7 +1704,7 @@ name = "nsstring"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3198,7 +3198,7 @@ dependencies = [
|
|||
"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
|
||||
"checksum ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc8393b3c7352f94092497f6b52019643e493b6b890eb417cdb7c46117e621"
|
||||
"checksum encoding_c 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "769ecb8b33323998e482b218c0d13cd64c267609023b4b7ec3ee740714c318ee"
|
||||
"checksum encoding_rs 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ca20350a7cb5aab5b9034731123d6d412caf3e92d4985e739e411ba0955fd0eb"
|
||||
"checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be"
|
||||
"checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum euclid 0.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "600657e7e5c03bfbccdc68721bc3b5abcb761553973387124eae9c9e4f02c210"
|
||||
|
|
|
@ -1028,7 +1028,7 @@ pref("security.sandbox.gpu.level", 0);
|
|||
pref("security.sandbox.gmp.win32k-disable", false);
|
||||
#endif
|
||||
|
||||
#if defined(NIGHTLY_BUILD) && defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
||||
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
||||
// Start the Mac sandbox early during child process startup instead
|
||||
// of when messaged by the parent after the message loop is running.
|
||||
pref("security.sandbox.content.mac.earlyinit", true);
|
||||
|
|
|
@ -318,7 +318,7 @@ var ContentBlocking = {
|
|||
XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
|
||||
this.PREF_REPORT_BREAKAGE_ENABLED, false);
|
||||
|
||||
this.appMenuLabel.setAttribute("label", this.strings.appMenuTitle);
|
||||
this.appMenuLabel.setAttribute("value", this.strings.appMenuTitle);
|
||||
this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip);
|
||||
|
||||
this.activeTooltipText =
|
||||
|
@ -347,6 +347,7 @@ var ContentBlocking = {
|
|||
return;
|
||||
}
|
||||
let button = document.getElementById("tracking-protection-preferences-button");
|
||||
let appMenuCategoryLabel = document.getElementById("appMenu-tp-category");
|
||||
let label;
|
||||
let category = Services.prefs.getStringPref(this.PREF_CB_CATEGORY);
|
||||
switch (category) {
|
||||
|
@ -360,6 +361,7 @@ var ContentBlocking = {
|
|||
label = gNavigatorBundle.getString("contentBlocking.category.custom");
|
||||
break;
|
||||
}
|
||||
appMenuCategoryLabel.value = label;
|
||||
button.label = label;
|
||||
},
|
||||
|
||||
|
|
|
@ -152,10 +152,6 @@ let gWhitelist = [{
|
|||
file: "pocket.properties",
|
||||
key: "tos",
|
||||
type: "double-quote",
|
||||
}, {
|
||||
file: "aboutNetworking.dtd",
|
||||
key: "aboutNetworking.logTutorial",
|
||||
type: "single-quote",
|
||||
}, {
|
||||
file: "browser.dtd",
|
||||
key: "addonPostInstallMessage.label",
|
||||
|
|
|
@ -4,6 +4,8 @@ const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
|
|||
const TPC_PREF = "network.cookie.cookieBehavior";
|
||||
const TT_PREF = "urlclassifier.trackingTable";
|
||||
|
||||
ChromeUtils.import("resource://testing-common/CustomizableUITestUtils.jsm", this);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(TP_PREF);
|
||||
Services.prefs.clearUserPref(TP_PB_PREF);
|
||||
|
@ -40,3 +42,31 @@ add_task(async function testCategoryLabelsInControlPanel() {
|
|||
"The preferencesButton label has been changed to custom");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testCategoryLabelsInAppMenu() {
|
||||
await BrowserTestUtils.withNewTab("http://www.example.com", async function() {
|
||||
let cuiTestUtils = new CustomizableUITestUtils(window);
|
||||
await cuiTestUtils.openMainMenu();
|
||||
|
||||
let appMenuCategoryLabel = document.getElementById("appMenu-tp-category");
|
||||
ok(appMenuCategoryLabel.value, "The appMenuCategory label exists");
|
||||
|
||||
Services.prefs.setStringPref(CAT_PREF, "strict");
|
||||
await TestUtils.waitForCondition(() => appMenuCategoryLabel.value ==
|
||||
gNavigatorBundle.getString("contentBlocking.category.strict"));
|
||||
is(appMenuCategoryLabel.value, gNavigatorBundle.getString("contentBlocking.category.strict"),
|
||||
"The appMenuCategory label has been changed to strict");
|
||||
|
||||
Services.prefs.setStringPref(CAT_PREF, "standard");
|
||||
await TestUtils.waitForCondition(() => appMenuCategoryLabel.value ==
|
||||
gNavigatorBundle.getString("contentBlocking.category.standard"));
|
||||
is(appMenuCategoryLabel.value, gNavigatorBundle.getString("contentBlocking.category.standard"),
|
||||
"The appMenuCategory label has been changed to standard");
|
||||
|
||||
Services.prefs.setStringPref(CAT_PREF, "custom");
|
||||
await TestUtils.waitForCondition(() => appMenuCategoryLabel.value ==
|
||||
gNavigatorBundle.getString("contentBlocking.category.custom"));
|
||||
is(appMenuCategoryLabel.value, gNavigatorBundle.getString("contentBlocking.category.custom"),
|
||||
"The appMenuCategory label has been changed to custom");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -225,9 +225,13 @@
|
|||
</toolbaritem>
|
||||
<toolbarseparator class="sync-ui-item"/>
|
||||
<toolbaritem>
|
||||
<toolbarbutton id="appMenu-tp-label"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
oncommand="ContentBlocking.openPreferences('appMenu-trackingprotection');"/>
|
||||
<toolbarbutton id="appMenu-tp-button"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
oncommand="ContentBlocking.openPreferences('appMenu-trackingprotection');">
|
||||
<image id="appMenu-tp-icon" class="toolbarbutton-icon"/>
|
||||
<label id="appMenu-tp-label" class="toolbarbutton-text"/>
|
||||
<label id="appMenu-tp-category"/>
|
||||
</toolbarbutton>
|
||||
</toolbaritem>
|
||||
<toolbarseparator id="appMenu-tp-separator"/>
|
||||
<toolbarbutton id="appMenu-new-window-button"
|
||||
|
|
|
@ -15,9 +15,11 @@ const TELEMETRY_RESULT_ENUM = {
|
|||
};
|
||||
|
||||
window.onload = function() {
|
||||
let defaultEngine = document.getElementById("defaultEngine");
|
||||
let defaultEngineParagraph = document.getElementById("defaultEngineParagraph");
|
||||
let originalDefault = Services.search.originalDefaultEngine;
|
||||
defaultEngine.textContent = originalDefault.name;
|
||||
document.l10n.setAttributes(defaultEngineParagraph, "page-info-new-search-engine",
|
||||
{ searchEngine: originalDefault.name });
|
||||
let defaultEngine = document.getElementById("defaultEngine");
|
||||
defaultEngine.style.backgroundImage =
|
||||
'url("' + originalDefault.iconURI.spec + '")';
|
||||
|
||||
|
|
|
@ -4,21 +4,12 @@
|
|||
- 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/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
<!ENTITY % searchresetDTD SYSTEM "chrome://browser/locale/aboutSearchReset.dtd">
|
||||
%searchresetDTD;
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
]>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<head>
|
||||
<title>&searchreset.tabtitle;</title>
|
||||
<title data-l10n-id="tab-title"/>
|
||||
<link rel="stylesheet" type="text/css" media="all"
|
||||
href="chrome://global/skin/in-content/info-pages.css"/>
|
||||
<link rel="stylesheet" type="text/css" media="all"
|
||||
|
@ -28,31 +19,35 @@
|
|||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/search/searchReset.js"/>
|
||||
<link rel="localization" href="browser/aboutSearchReset.ftl"/>
|
||||
<link rel="localization" href="branding/brand.ftl"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<div class="title">
|
||||
<h1 class="title-text">&searchreset.pageTitle;</h1>
|
||||
<h1 class="title-text" data-l10n-id="page-title"/>
|
||||
</div>
|
||||
|
||||
<div class="description">
|
||||
<p>&searchreset.pageInfo1;</p>
|
||||
<p>&searchreset.selector.label;<span id="defaultEngine"/></p>
|
||||
<p data-l10n-id="page-info-outofdate"/>
|
||||
<p id="defaultEngineParagraph">
|
||||
<span id="defaultEngine" data-l10n-name="default-engine"/>
|
||||
</p>
|
||||
|
||||
<p>&searchreset.beforelink.pageInfo2;<a id="linkSettingsPage" href="about:preferences#search">&searchreset.link.pageInfo2;</a>&searchreset.afterlink.pageInfo2;</p>
|
||||
<p data-l10n-id="page-info-how-to-change">
|
||||
<a id="linkSettingsPage" href="about:preferences#search" data-l10n-name="link"></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="button-container">
|
||||
<xul:button id="searchResetKeepCurrent"
|
||||
label="&searchreset.noChangeButton;"
|
||||
accesskey="&searchreset.noChangeButton.access;"
|
||||
data-l10n-id="no-change-button"
|
||||
oncommand="keepCurrentEngine();"/>
|
||||
<xul:button class="primary"
|
||||
id="searchResetChangeEngine"
|
||||
label="&searchreset.changeEngineButton;"
|
||||
accesskey="&searchreset.changeEngineButton.access;"
|
||||
data-l10n-id="change-engine-button"
|
||||
oncommand="changeSearchEngine();"/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,19 +6,27 @@
|
|||
function synthesizeMouseOver(element) {
|
||||
window.windowUtils.disableNonTestMouseEvents(true);
|
||||
|
||||
let promise = BrowserTestUtils.waitForEvent(gURLBar.inputField, "mouseover");
|
||||
|
||||
EventUtils.synthesizeMouse(element, 1, 1, {type: "mouseover"});
|
||||
EventUtils.synthesizeMouse(element, 2, 2, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouse(element, 3, 3, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouse(element, 4, 4, {type: "mousemove"});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function synthesizeMouseOut(element) {
|
||||
let promise = BrowserTestUtils.waitForEvent(gURLBar.inputField, "mouseout");
|
||||
|
||||
EventUtils.synthesizeMouse(element, 0, 0, {type: "mouseout"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
|
||||
window.windowUtils.disableNonTestMouseEvents(false);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function expectTooltip(text) {
|
||||
|
@ -28,24 +36,24 @@ async function expectTooltip(text) {
|
|||
let element = gURLBar.inputField;
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(tooltip, "popupshown");
|
||||
synthesizeMouseOver(element);
|
||||
await synthesizeMouseOver(element);
|
||||
await popupShownPromise;
|
||||
|
||||
is(element.getAttribute("title"), text, "title attribute has expected text");
|
||||
is(tooltip.textContent, text, "tooltip shows expected text");
|
||||
|
||||
synthesizeMouseOut(element);
|
||||
await synthesizeMouseOut(element);
|
||||
}
|
||||
|
||||
async function expectNoTooltip() {
|
||||
ok(!gURLBar.hasAttribute("textoverflow"), "Urlbar isn't overflowing");
|
||||
|
||||
let element = gURLBar.inputField;
|
||||
synthesizeMouseOver(element);
|
||||
await synthesizeMouseOver(element);
|
||||
|
||||
is(element.getAttribute("title"), null, "title attribute shouldn't be set");
|
||||
|
||||
synthesizeMouseOut(element);
|
||||
await synthesizeMouseOut(element);
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# 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/.
|
||||
|
||||
tab-title = Restore Search Settings
|
||||
page-title = Restore your search settings?
|
||||
page-info-outofdate = Your search settings might be out-of-date. { -brand-short-name } can help you restore the default search settings.
|
||||
# Variables:
|
||||
# $searchEngine (String) - Name of the default search engine e.g. Google
|
||||
page-info-new-search-engine = This will set your default search engine to <span data-l10n-name="default-engine">{ $searchEngine }</span>
|
||||
page-info-how-to-change = You can change these settings at any time from the <a data-l10n-name="link">Settings page</a>.
|
||||
no-change-button =
|
||||
.label = Don’t Change
|
||||
.accesskey = D
|
||||
change-engine-button =
|
||||
.label = Change Search Engine
|
||||
.accesskey = C
|
|
@ -1,30 +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/. -->
|
||||
|
||||
<!ENTITY searchreset.tabtitle "Restore Search Settings">
|
||||
|
||||
<!ENTITY searchreset.pageTitle "Restore your search settings?">
|
||||
|
||||
<!ENTITY searchreset.pageInfo1 "Your search settings might be out-of-date. &brandShortName; can help you restore the default search settings.">
|
||||
|
||||
|
||||
<!-- LOCALIZATION NOTE (searchreset.selector.label): this string is
|
||||
followed by a dropdown of all the built-in search engines. -->
|
||||
<!ENTITY searchreset.selector.label "This will set your default search engine to">
|
||||
|
||||
<!-- LOCALIZATION NOTE (searchreset.beforelink.pageInfo,
|
||||
searchreset.afterlink.pageInfo): these two string are used respectively
|
||||
before and after the "Settings page" link (searchreset.link.pageInfo2).
|
||||
Localizers can use one of them, or both, to better adapt this sentence to
|
||||
their language. -->
|
||||
<!ENTITY searchreset.beforelink.pageInfo2 "You can change these settings at any time from the ">
|
||||
<!ENTITY searchreset.afterlink.pageInfo2 ".">
|
||||
|
||||
<!ENTITY searchreset.link.pageInfo2 "Settings page">
|
||||
|
||||
<!ENTITY searchreset.noChangeButton "Don’t Change">
|
||||
<!ENTITY searchreset.noChangeButton.access "D">
|
||||
|
||||
<!ENTITY searchreset.changeEngineButton "Change Search Engine">
|
||||
<!ENTITY searchreset.changeEngineButton.access "C">
|
|
@ -1019,14 +1019,14 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
|||
<!-- LOCALIZATION NOTE (trackingProtection.unblock5.label, trackingProtection.unblock5.accesskey):
|
||||
The associated button with this label and accesskey is only shown when opening the control
|
||||
center while looking at a site with trackers in NON-private browsing mode. -->
|
||||
<!ENTITY trackingProtection.unblock5.label "Turn off blocking for this site">
|
||||
<!ENTITY trackingProtection.unblock5.label "Turn off Blocking for This Site">
|
||||
<!ENTITY trackingProtection.unblock5.accesskey "T">
|
||||
<!-- LOCALIZATION NOTE (trackingProtection.unblockPrivate6.label, trackingProtection.unblockPrivate6.accesskey):
|
||||
The associated button with this label and accesskey is only shown when opening the control
|
||||
center while looking at a site with trackers in PRIVATE browsing mode. -->
|
||||
<!ENTITY trackingProtection.unblockPrivate5.label "Turn off blocking temporarily">
|
||||
<!ENTITY trackingProtection.unblockPrivate5.label "Turn off Blocking Temporarily">
|
||||
<!ENTITY trackingProtection.unblockPrivate5.accesskey "T">
|
||||
<!ENTITY trackingProtection.block6.label "Turn on blocking for this site">
|
||||
<!ENTITY trackingProtection.block6.label "Turn on Blocking for This Site">
|
||||
<!ENTITY trackingProtection.block6.accesskey "T">
|
||||
<!ENTITY trackingProtection.reload2.label "Reload Page">
|
||||
<!ENTITY trackingProtection.reload2.accesskey "R">
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
locale/browser/aboutPrivateBrowsing.dtd (%chrome/browser/aboutPrivateBrowsing.dtd)
|
||||
locale/browser/aboutRobots.dtd (%chrome/browser/aboutRobots.dtd)
|
||||
locale/browser/accounts.properties (%chrome/browser/accounts.properties)
|
||||
locale/browser/aboutSearchReset.dtd (%chrome/browser/aboutSearchReset.dtd)
|
||||
locale/browser/aboutTabCrashed.dtd (%chrome/browser/aboutTabCrashed.dtd)
|
||||
locale/browser/browser.dtd (%chrome/browser/browser.dtd)
|
||||
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
|
||||
|
|
|
@ -1051,6 +1051,13 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
let origins = new Set();
|
||||
while (perms.length) {
|
||||
let perm = perms.shift();
|
||||
// Let's make sure that we're not looking at a permission for
|
||||
// https://exampletracker.company when we mean to look for the
|
||||
// permisison for https://exampletracker.com!
|
||||
if (perm.type != prefix &&
|
||||
!perm.type.startsWith(`${prefix}^`)) {
|
||||
continue;
|
||||
}
|
||||
origins.add(perm.principal.origin);
|
||||
}
|
||||
return origins.size;
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
}
|
||||
|
||||
.identity-popup-preferences-button:-moz-locale-dir(rtl) {
|
||||
background-position: center left;
|
||||
background-position: center left 8px;
|
||||
}
|
||||
|
||||
.identity-popup-preferences-button > .toolbarbutton-text {
|
||||
|
|
|
@ -596,8 +596,23 @@ toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton
|
|||
#appMenu-tp-label {
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
list-style-image: url(chrome://browser/skin/tracking-protection.svg);
|
||||
-moz-box-flex: 1;
|
||||
padding: 0;
|
||||
padding-inline-start: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#appMenu-tp-icon {
|
||||
list-style-image: url(chrome://browser/skin/tracking-protection.svg);
|
||||
}
|
||||
|
||||
#appMenu-tp-button {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
#appMenu-tp-category {
|
||||
color: var(--panel-disabled-color);
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.addon-banner-item > .toolbarbutton-text,
|
||||
|
|
|
@ -1581,17 +1581,19 @@ def security_hardening_cflags(hardening_flag, asan, optimize, c_compiler, target
|
|||
js_flags.append("-U_FORTIFY_SOURCE")
|
||||
js_flags.append("-D_FORTIFY_SOURCE=2")
|
||||
|
||||
# fstack-protector ------------------------------------
|
||||
# Enable only if hardening is not disabled and ASAN is
|
||||
# not on as ASAN will catch the crashes for us
|
||||
if compiler_is_gccish and not asan:
|
||||
# mingw-clang cross-compile toolchain has bugs with stack protector
|
||||
if target.os != 'WINNT' or c_compiler == 'gcc':
|
||||
flags.append("-fstack-protector-strong")
|
||||
|
||||
# If ASAN _is_ on, undefine FOTIFY_SOURCE just to be safe
|
||||
if asan:
|
||||
flags.append("-U_FORTIFY_SOURCE")
|
||||
js_flags.append("-U_FORTIFY_SOURCE")
|
||||
|
||||
# fstack-protector ------------------------------------
|
||||
# Enable only if --enable-hardening is passed and ASAN is
|
||||
# not on as ASAN will catch the crashes for us
|
||||
if hardening_flag and compiler_is_gccish and not asan:
|
||||
flags.append("-fstack-protector-strong")
|
||||
|
||||
# fno-common -----------------------------------------
|
||||
# Do not merge variables for ASAN; can detect some subtle bugs
|
||||
if asan:
|
||||
|
|
|
@ -14,11 +14,15 @@ add_old_configure_assignment(
|
|||
|
||||
|
||||
# GCC/Clang warnings:
|
||||
# https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Warning-Options.html
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
|
||||
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
||||
|
||||
# lots of useful warnings
|
||||
add_gcc_warning('-Wall')
|
||||
|
||||
# catch implicit truncation of enum values assigned to smaller bit fields
|
||||
check_and_add_gcc_warning('-Wbitfield-enum-conversion')
|
||||
|
||||
# catches bugs, e.g. "if (c); foo();", few false positives
|
||||
add_gcc_warning('-Wempty-body')
|
||||
|
||||
|
|
|
@ -80,7 +80,10 @@ class App extends PureComponent {
|
|||
}
|
||||
|
||||
const isRuntimeAvailable = id => {
|
||||
const runtimes = this.props.usbRuntimes;
|
||||
const runtimes = [
|
||||
...this.props.networkRuntimes,
|
||||
...this.props.usbRuntimes,
|
||||
];
|
||||
return !!runtimes.find(x => x.id === id);
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ const SUPPORTED_TARGET_BY_RUNTIME = {
|
|||
DEBUG_TARGETS.TAB,
|
||||
],
|
||||
[RUNTIMES.NETWORK]: [
|
||||
DEBUG_TARGETS.EXTENSION,
|
||||
DEBUG_TARGETS.TAB,
|
||||
],
|
||||
};
|
||||
|
@ -44,6 +45,7 @@ const SUPPORTED_TARGET_PANE_BY_RUNTIME = {
|
|||
DEBUG_TARGET_PANE.TAB,
|
||||
],
|
||||
[RUNTIMES.NETWORK]: [
|
||||
DEBUG_TARGET_PANE.INSTALLED_EXTENSION,
|
||||
DEBUG_TARGET_PANE.TAB,
|
||||
],
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ prefs =
|
|||
support-files =
|
||||
debug-target-pane_collapsibilities_head.js
|
||||
head-addons-script.js
|
||||
head-mocks.js
|
||||
head.js
|
||||
mocks/*
|
||||
resources/test-adb-extension/*
|
||||
|
@ -17,7 +18,7 @@ support-files =
|
|||
!/devtools/client/shared/test/shared-redux-head.js
|
||||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||
|
||||
[browser_aboutdebugging_addons_usb_runtime.js]
|
||||
[browser_aboutdebugging_addons_remote_runtime.js]
|
||||
[browser_aboutdebugging_connect_networklocations.js]
|
||||
[browser_aboutdebugging_connect_toggle_usb_devices.js]
|
||||
skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
|
||||
|
@ -30,6 +31,7 @@ skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug
|
|||
[browser_aboutdebugging_navigate.js]
|
||||
[browser_aboutdebugging_persist_connection.js]
|
||||
[browser_aboutdebugging_routes.js]
|
||||
[browser_aboutdebugging_select_network_runtime.js]
|
||||
[browser_aboutdebugging_sidebar_network_runtimes.js]
|
||||
[browser_aboutdebugging_sidebar_usb_runtime.js]
|
||||
[browser_aboutdebugging_sidebar_usb_runtime_connect.js]
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const NETWORK_RUNTIME_HOST = "localhost:6080";
|
||||
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
|
||||
const USB_RUNTIME_ID = "test-runtime-id";
|
||||
const USB_RUNTIME_DEVICE_NAME = "test device name";
|
||||
const USB_RUNTIME_APP_NAME = "TestUsbApp";
|
||||
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
// Test that addons are displayed and updated for USB runtimes when expected.
|
||||
add_task(async function() {
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
info("Prepare USB client mock");
|
||||
const usbClient = mocks.createUSBRuntime(USB_RUNTIME_ID, {
|
||||
deviceName: USB_RUNTIME_DEVICE_NAME,
|
||||
name: USB_RUNTIME_APP_NAME,
|
||||
});
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
info("Test addons in runtime page for USB client");
|
||||
await connectToRuntime(USB_RUNTIME_DEVICE_NAME, document);
|
||||
await selectRuntime(USB_RUNTIME_DEVICE_NAME, USB_RUNTIME_APP_NAME, document);
|
||||
await testAddonsOnMockedRemoteClient(usbClient, mocks.thisFirefoxClient, document);
|
||||
|
||||
info("Prepare Network client mock");
|
||||
const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, {
|
||||
name: NETWORK_RUNTIME_APP_NAME,
|
||||
});
|
||||
|
||||
info("Test addons in runtime page for Network client");
|
||||
await connectToRuntime(NETWORK_RUNTIME_HOST, document);
|
||||
await selectRuntime(NETWORK_RUNTIME_HOST, NETWORK_RUNTIME_APP_NAME, document);
|
||||
await testAddonsOnMockedRemoteClient(networkClient, mocks.thisFirefoxClient, document);
|
||||
|
||||
await removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* Check that addons are visible in the runtime page for a remote client (USB or network).
|
||||
*/
|
||||
async function testAddonsOnMockedRemoteClient(remoteClient, firefoxClient, document) {
|
||||
const extensionPane = getDebugTargetPane("Extensions", document);
|
||||
info("Check an empty target pane message is displayed");
|
||||
ok(extensionPane.querySelector(".js-debug-target-list-empty"),
|
||||
"Extensions list is empty");
|
||||
|
||||
info("Add an extension to the remote client");
|
||||
const addon = { name: "Test extension name", debuggable: true };
|
||||
remoteClient.listAddons = () => [addon];
|
||||
remoteClient._eventEmitter.emit("addonListChanged");
|
||||
|
||||
info("Wait until the extension appears");
|
||||
await waitUntil(() => !extensionPane.querySelector(".js-debug-target-list-empty"));
|
||||
|
||||
const extensionTarget = findDebugTargetByText("Test extension name", document);
|
||||
ok(extensionTarget, "Extension target appeared for the remote runtime");
|
||||
|
||||
// The goal here is to check that runtimes addons are only updated when the remote
|
||||
// runtime is sending addonListChanged events. The reason for this test is because the
|
||||
// previous implementation was updating the remote runtime extensions list when the
|
||||
// _local_ AddonManager was updated.
|
||||
info("Remove the extension from the remote client WITHOUT sending an event");
|
||||
remoteClient.listAddons = () => [];
|
||||
|
||||
info("Simulate an addon update on the ThisFirefox client");
|
||||
firefoxClient._eventEmitter.emit("addonListChanged");
|
||||
|
||||
// To avoid wait for a set period of time we trigger another async update, adding a new
|
||||
// tab. We assume that if the addon update mechanism had started, it would also be done
|
||||
// when the new tab was processed.
|
||||
info("Wait until the tab target for 'http://some.random/url.com' appears");
|
||||
const testTab = { outerWindowID: 0, url: "http://some.random/url.com" };
|
||||
remoteClient.listTabs = () => ({ tabs: [testTab] });
|
||||
remoteClient._eventEmitter.emit("tabListChanged");
|
||||
await waitUntil(() => findDebugTargetByText("http://some.random/url.com", document));
|
||||
|
||||
ok(findDebugTargetByText("Test extension name", document),
|
||||
"The test extension is still visible");
|
||||
|
||||
info("Emit `addonListChanged` on remoteClient and wait for the target list to update");
|
||||
remoteClient._eventEmitter.emit("addonListChanged");
|
||||
await waitUntil(() => !findDebugTargetByText("Test extension name", document));
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/* 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 addons are displayed and updated for USB runtimes when expected.
|
||||
add_task(async function() {
|
||||
const usbMocks = new UsbMocks();
|
||||
usbMocks.enableMocks();
|
||||
registerCleanupFunction(() => usbMocks.disableMocks());
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
const usbClient = usbMocks.createRuntime(RUNTIME_ID, {
|
||||
deviceName: RUNTIME_DEVICE_NAME,
|
||||
name: RUNTIME_APP_NAME,
|
||||
});
|
||||
usbMocks.emitUpdate();
|
||||
|
||||
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");
|
||||
ok(extensionPane.querySelector(".js-debug-target-list-empty"),
|
||||
"Extensions list is empty");
|
||||
|
||||
info("Add an extension to the remote client");
|
||||
const addon = { name: "Test extension name", debuggable: true };
|
||||
usbClient.listAddons = () => [addon];
|
||||
usbClient._eventEmitter.emit("addonListChanged");
|
||||
|
||||
info("Wait until the extension appears");
|
||||
await waitUntil(() => !extensionPane.querySelector(".js-debug-target-list-empty"));
|
||||
|
||||
const extensionTarget = findDebugTargetByText("Test extension name", document);
|
||||
ok(extensionTarget, "Extension target appeared for the USB runtime");
|
||||
|
||||
// The goal here is to check that USB runtimes addons are only updated when the USB
|
||||
// runtime is sending addonListChanged events. The reason for this test is because the
|
||||
// previous implementation was updating the USB runtime extensions list when the _local_
|
||||
// AddonManager was updated.
|
||||
info("Remove the extension from the remote client WITHOUT sending an event");
|
||||
usbClient.listAddons = () => [];
|
||||
|
||||
info("Simulate an addon update on the ThisFirefox client");
|
||||
usbMocks.thisFirefoxClient._eventEmitter.emit("addonListChanged");
|
||||
|
||||
// To avoid wait for a set period of time we trigger another async update, adding a new
|
||||
// tab. We assume that if the addon update mechanism had started, it would also be done
|
||||
// when the new tab was processed.
|
||||
info("Wait until the tab target for 'http://some.random/url.com' appears");
|
||||
const testTab = { outerWindowID: 0, url: "http://some.random/url.com" };
|
||||
usbClient.listTabs = () => ({ tabs: [testTab] });
|
||||
usbClient._eventEmitter.emit("tabListChanged");
|
||||
await waitUntil(() => findDebugTargetByText("http://some.random/url.com", document));
|
||||
|
||||
ok(findDebugTargetByText("Test extension name", document),
|
||||
"The test extension is still visible");
|
||||
|
||||
info("Emit `addonListChanged` on usbClient and wait for the target list to update");
|
||||
usbClient._eventEmitter.emit("addonListChanged");
|
||||
await waitUntil(() => !findDebugTargetByText("Test extension name", document));
|
||||
|
||||
await removeTab(tab);
|
||||
});
|
|
@ -7,24 +7,20 @@ 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);
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
// Test that the expected supported categories are displayed for USB runtimes.
|
||||
add_task(async function() {
|
||||
const usbMocks = new UsbMocks();
|
||||
usbMocks.enableMocks();
|
||||
registerCleanupFunction(() => {
|
||||
usbMocks.disableMocks();
|
||||
});
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
usbMocks.createRuntime(RUNTIME_ID, {
|
||||
mocks.createUSBRuntime(RUNTIME_ID, {
|
||||
deviceName: RUNTIME_DEVICE_NAME,
|
||||
name: RUNTIME_APP_NAME,
|
||||
});
|
||||
usbMocks.emitUpdate();
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
await connectToRuntime(RUNTIME_DEVICE_NAME, document);
|
||||
await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
|
||||
|
@ -44,8 +40,8 @@ add_task(async function() {
|
|||
}
|
||||
|
||||
info("Remove USB runtime");
|
||||
usbMocks.removeRuntime(RUNTIME_ID);
|
||||
usbMocks.emitUpdate();
|
||||
mocks.removeUSBRuntime(RUNTIME_ID);
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
info("Wait until the USB sidebar item disappears");
|
||||
await waitUntil(() => !findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
|
||||
|
|
|
@ -7,29 +7,27 @@ 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);
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-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());
|
||||
const mocks = new Mocks();
|
||||
|
||||
let { document, tab } = await openAboutDebugging();
|
||||
|
||||
const usbClient = usbMocks.createRuntime(RUNTIME_ID, {
|
||||
const usbClient = mocks.createUSBRuntime(RUNTIME_ID, {
|
||||
name: RUNTIME_APP_NAME,
|
||||
deviceName: RUNTIME_DEVICE_NAME,
|
||||
});
|
||||
usbMocks.emitUpdate();
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
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();
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
info("Wait until the remote runtime appears as connected");
|
||||
await waitUntil(() => {
|
||||
|
@ -40,7 +38,7 @@ add_task(async function() {
|
|||
// 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);
|
||||
mocks.removeUSBRuntime(RUNTIME_ID);
|
||||
|
||||
info("Emit 'closed' on the client and wait for the sidebar item to disappear");
|
||||
usbClient._eventEmitter.emit("closed");
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from mocks/head-usb-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
/**
|
||||
* Test that the initial route is /runtime/this-firefox
|
||||
|
@ -22,9 +22,7 @@ add_task(async function() {
|
|||
*/
|
||||
add_task(async function() {
|
||||
// enable USB devices mocks
|
||||
const usbMocks = new UsbMocks();
|
||||
usbMocks.enableMocks();
|
||||
registerCleanupFunction(() => usbMocks.disableMocks());
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
|
@ -43,11 +41,11 @@ add_task(async function() {
|
|||
|
||||
info("Check 'USB device runtime' page");
|
||||
// connect to a mocked USB runtime
|
||||
usbMocks.createRuntime("1337id", {
|
||||
mocks.createUSBRuntime("1337id", {
|
||||
deviceName: "Fancy Phone",
|
||||
name: "Lorem ipsum",
|
||||
});
|
||||
usbMocks.emitUpdate();
|
||||
mocks.emitUSBUpdate();
|
||||
await connectToRuntime("Fancy Phone", document);
|
||||
// navigate to it via URL
|
||||
document.location.hash = "#/runtime/1337id";
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const NETWORK_RUNTIME_HOST = "localhost:6080";
|
||||
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
|
||||
const NETWORK_RUNTIME_CHANNEL = "SomeChannel";
|
||||
const NETWORK_RUNTIME_VERSION = "12.3";
|
||||
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
// Test that network runtimes can be selected.
|
||||
add_task(async function() {
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
info("Prepare Network client mock");
|
||||
const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, {
|
||||
name: NETWORK_RUNTIME_APP_NAME,
|
||||
});
|
||||
networkClient.getDeviceDescription = () => {
|
||||
return {
|
||||
name: NETWORK_RUNTIME_APP_NAME,
|
||||
channel: NETWORK_RUNTIME_CHANNEL,
|
||||
version: NETWORK_RUNTIME_VERSION,
|
||||
};
|
||||
};
|
||||
|
||||
info("Test addons in runtime page for Network client");
|
||||
await connectToRuntime(NETWORK_RUNTIME_HOST, document);
|
||||
await selectRuntime(NETWORK_RUNTIME_HOST, NETWORK_RUNTIME_APP_NAME, document);
|
||||
|
||||
info("Check that the network runtime mock is properly displayed");
|
||||
const thisFirefoxRuntimeInfo = document.querySelector(".js-runtime-info");
|
||||
ok(thisFirefoxRuntimeInfo, "Runtime info for this-firefox runtime is displayed");
|
||||
const runtimeInfoText = thisFirefoxRuntimeInfo.textContent;
|
||||
|
||||
ok(runtimeInfoText.includes(NETWORK_RUNTIME_APP_NAME),
|
||||
"network runtime info shows the correct runtime name: " + runtimeInfoText);
|
||||
ok(runtimeInfoText.includes(NETWORK_RUNTIME_VERSION),
|
||||
"network runtime info shows the correct version number: " + runtimeInfoText);
|
||||
|
||||
await removeTab(tab);
|
||||
});
|
|
@ -3,24 +3,20 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from mocks/head-usb-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
const RUNTIME_ID = "test-runtime-id";
|
||||
const RUNTIME_DEVICE_NAME = "test device name";
|
||||
|
||||
// Test that USB runtimes appear and disappear from the sidebar.
|
||||
add_task(async function() {
|
||||
const usbMocks = new UsbMocks();
|
||||
usbMocks.enableMocks();
|
||||
registerCleanupFunction(() => {
|
||||
usbMocks.disableMocks();
|
||||
});
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
usbMocks.createRuntime(RUNTIME_ID, { deviceName: RUNTIME_DEVICE_NAME });
|
||||
usbMocks.emitUpdate();
|
||||
mocks.createUSBRuntime(RUNTIME_ID, { deviceName: RUNTIME_DEVICE_NAME });
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
info("Wait until the USB sidebar item appears");
|
||||
await waitUntil(() => findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
|
||||
|
@ -33,8 +29,8 @@ add_task(async function() {
|
|||
await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button"));
|
||||
|
||||
info("Remove all USB runtimes");
|
||||
usbMocks.removeRuntime(RUNTIME_ID);
|
||||
usbMocks.emitUpdate();
|
||||
mocks.removeUSBRuntime(RUNTIME_ID);
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
info("Wait until the USB sidebar item disappears");
|
||||
await waitUntil(() => !findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
|
||||
|
|
|
@ -10,34 +10,32 @@ const RUNTIME_APP_NAME = "TestApp";
|
|||
const OTHER_RUNTIME_ID = "other-runtime-id";
|
||||
const OTHER_RUNTIME_APP_NAME = "OtherApp";
|
||||
|
||||
/* import-globals-from mocks/head-usb-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
|
||||
/* import-globals-from head-mocks.js */
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-mocks.js", this);
|
||||
|
||||
// Test that USB runtimes are ot disconnected on refresh.
|
||||
add_task(async function() {
|
||||
const usbMocks = new UsbMocks();
|
||||
usbMocks.enableMocks();
|
||||
registerCleanupFunction(() => usbMocks.disableMocks());
|
||||
const mocks = new Mocks();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
info("Create a first runtime and connect to it");
|
||||
usbMocks.createRuntime(RUNTIME_ID, {
|
||||
mocks.createUSBRuntime(RUNTIME_ID, {
|
||||
deviceName: RUNTIME_DEVICE_NAME,
|
||||
name: RUNTIME_APP_NAME,
|
||||
});
|
||||
usbMocks.emitUpdate();
|
||||
mocks.emitUSBUpdate();
|
||||
|
||||
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, {
|
||||
mocks.createUSBRuntime(OTHER_RUNTIME_ID, {
|
||||
deviceName: OTHER_RUNTIME_APP_NAME,
|
||||
});
|
||||
|
||||
// Mock the refreshUSBRuntimes to emit an update.
|
||||
usbMocks.usbRuntimesMock.refreshUSBRuntimes = () => usbMocks.emitUpdate();
|
||||
mocks.usbRuntimesMock.refreshUSBRuntimes = () => mocks.emitUSBUpdate();
|
||||
document.querySelector(".js-refresh-devices-button").click();
|
||||
|
||||
info(`Wait until the sidebar item for ${OTHER_RUNTIME_APP_NAME} appears`);
|
||||
|
|
|
@ -7,31 +7,32 @@ const MOCKS_ROOT = CHROME_URL_ROOT + "mocks/";
|
|||
|
||||
const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
|
||||
|
||||
/* import-globals-from head-client-wrapper-mock.js */
|
||||
/* import-globals-from mocks/head-client-wrapper-mock.js */
|
||||
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-client-wrapper-mock.js", this);
|
||||
/* import-globals-from head-runtime-client-factory-mock.js */
|
||||
/* import-globals-from mocks/head-runtime-client-factory-mock.js */
|
||||
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-runtime-client-factory-mock.js",
|
||||
this);
|
||||
/* import-globals-from head-usb-runtimes-mock.js */
|
||||
/* import-globals-from mocks/head-usb-runtimes-mock.js */
|
||||
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-usb-runtimes-mock.js", this);
|
||||
|
||||
/**
|
||||
* This wrapper around the USB mocks used in about:debugging tests provides helpers to
|
||||
* quickly setup mocks for typical USB runtime tests.
|
||||
* This wrapper around the mocks used in about:debugging tests provides helpers to
|
||||
* quickly setup mocks for runtime tests involving USB, network or wifi runtimes that can
|
||||
* are difficult to setup in a test environment.
|
||||
*/
|
||||
class UsbMocks {
|
||||
class Mocks {
|
||||
constructor() {
|
||||
// Setup the usb-runtimes mock to rely on the internal _runtimes array.
|
||||
// Setup the usb-runtimes mock to rely on the internal _usbRuntimes array.
|
||||
this.usbRuntimesMock = createUsbRuntimesMock();
|
||||
this._runtimes = [];
|
||||
this._usbRuntimes = [];
|
||||
this.usbRuntimesMock.getUSBRuntimes = () => {
|
||||
return this._runtimes;
|
||||
return this._usbRuntimes;
|
||||
};
|
||||
|
||||
// refreshUSBRuntimes normally starts scan, which should ultimately fire the
|
||||
// "runtime-list-updated" event.
|
||||
this.usbRuntimesMock.refreshUSBRuntimes = () => {
|
||||
this.emitUpdate();
|
||||
this.emitUSBUpdate();
|
||||
};
|
||||
|
||||
// Prepare a fake observer to be able to emit events from this mock.
|
||||
|
@ -39,15 +40,23 @@ class UsbMocks {
|
|||
|
||||
// Setup the runtime-client-factory mock to rely on the internal _clients map.
|
||||
this.runtimeClientFactoryMock = createRuntimeClientFactoryMock();
|
||||
this._clients = {};
|
||||
this._clients = {
|
||||
[RUNTIMES.NETWORK]: {},
|
||||
[RUNTIMES.THIS_FIREFOX]: {},
|
||||
[RUNTIMES.USB]: {},
|
||||
};
|
||||
this.runtimeClientFactoryMock.createClientForRuntime = runtime => {
|
||||
return this._clients[runtime.id];
|
||||
return this._clients[runtime.type][runtime.id];
|
||||
};
|
||||
|
||||
// Add a client for THIS_FIREFOX, since about:debugging will start on the This Firefox
|
||||
// page.
|
||||
this._thisFirefoxClient = createThisFirefoxClientMock();
|
||||
this._clients[RUNTIMES.THIS_FIREFOX] = this._thisFirefoxClient;
|
||||
this._clients[RUNTIMES.THIS_FIREFOX][RUNTIMES.THIS_FIREFOX] = this._thisFirefoxClient;
|
||||
|
||||
// Enable mocks and remove them after the test.
|
||||
this.enableMocks();
|
||||
registerCleanupFunction(() => this.disableMocks());
|
||||
}
|
||||
|
||||
get thisFirefoxClient() {
|
||||
|
@ -62,9 +71,40 @@ class UsbMocks {
|
|||
disableMocks() {
|
||||
disableUsbRuntimesMock();
|
||||
disableRuntimeClientFactoryMock();
|
||||
|
||||
for (const host of Object.keys(this._clients[RUNTIMES.NETWORK])) {
|
||||
this.removeNetworkRuntime(host);
|
||||
}
|
||||
}
|
||||
|
||||
emitUpdate() {
|
||||
createNetworkRuntime(host, runtimeInfo) {
|
||||
const { addNetworkLocation } =
|
||||
require("devtools/client/aboutdebugging-new/src/modules/network-locations");
|
||||
addNetworkLocation(host);
|
||||
|
||||
// Add a valid client that can be returned for this particular runtime id.
|
||||
const mockNetworkClient = createClientMock();
|
||||
mockNetworkClient.getDeviceDescription = () => {
|
||||
return {
|
||||
name: runtimeInfo.name || "TestBrand",
|
||||
channel: runtimeInfo.channel || "release",
|
||||
version: runtimeInfo.version || "1.0",
|
||||
};
|
||||
};
|
||||
this._clients[RUNTIMES.NETWORK][host] = mockNetworkClient;
|
||||
|
||||
return mockNetworkClient;
|
||||
}
|
||||
|
||||
removeNetworkRuntime(host) {
|
||||
const { removeNetworkLocation } =
|
||||
require("devtools/client/aboutdebugging-new/src/modules/network-locations");
|
||||
removeNetworkLocation(host);
|
||||
|
||||
delete this._clients[RUNTIMES.NETWORK][host];
|
||||
}
|
||||
|
||||
emitUSBUpdate() {
|
||||
this._observerMock.emit("runtime-list-updated");
|
||||
}
|
||||
|
||||
|
@ -82,9 +122,9 @@ class UsbMocks {
|
|||
* @return {Object} Returns the mock client created for this runtime so that methods
|
||||
* can be overridden on it.
|
||||
*/
|
||||
createRuntime(id, runtimeInfo = {}) {
|
||||
createUSBRuntime(id, runtimeInfo = {}) {
|
||||
// Add a new runtime to the list of scanned runtimes.
|
||||
this._runtimes.push({
|
||||
this._usbRuntimes.push({
|
||||
id: id,
|
||||
_socketPath: runtimeInfo.socketPath || "test/path",
|
||||
deviceName: runtimeInfo.deviceName || "test device name",
|
||||
|
@ -100,13 +140,13 @@ class UsbMocks {
|
|||
version: runtimeInfo.version || "1.0",
|
||||
};
|
||||
};
|
||||
this._clients[id] = mockUsbClient;
|
||||
this._clients[RUNTIMES.USB][id] = mockUsbClient;
|
||||
|
||||
return mockUsbClient;
|
||||
}
|
||||
|
||||
removeRuntime(id) {
|
||||
this._runtimes = this._runtimes.filter(runtime => runtime.id !== id);
|
||||
delete this._clients[id];
|
||||
removeUSBRuntime(id) {
|
||||
this._usbRuntimes = this._usbRuntimes.filter(runtime => runtime.id !== id);
|
||||
delete this._clients[RUNTIMES.USB][id];
|
||||
}
|
||||
}
|
|
@ -183,6 +183,6 @@ async function selectRuntime(deviceName, name, document) {
|
|||
|
||||
await waitUntil(() => {
|
||||
const runtimeInfo = document.querySelector(".js-runtime-info");
|
||||
return runtimeInfo.textContent.includes(name);
|
||||
return runtimeInfo && runtimeInfo.textContent.includes(name);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-env browser */
|
||||
/* exported startup, shutdown, install, uninstall */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This function is called from the webconsole test:
|
||||
// browser_addons_debug_bootstrapped.js
|
||||
function myBootstrapAddonFunction() { // eslint-disable-line no-unused-vars
|
||||
Services.obs.notifyObservers(null, "addon-console-works");
|
||||
}
|
||||
|
||||
function startup() {
|
||||
Services.obs.notifyObservers(null, "test-devtools");
|
||||
}
|
||||
function shutdown() {}
|
||||
function install() {}
|
||||
function uninstall() {}
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.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/.
|
||||
-->
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest"
|
||||
em:id="test-devtools@mozilla.org"
|
||||
em:name="test-devtools"
|
||||
em:version="1.0"
|
||||
em:type="2"
|
||||
em:creator="Mozilla">
|
||||
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>44.0a1</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "test-devtools",
|
||||
"version": "1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "test-devtools@mozilla.org"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,7 @@ tags = devtools
|
|||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
addons/unpacked/bootstrap.js
|
||||
addons/unpacked/install.rdf
|
||||
addons/unpacked/manifest.json
|
||||
addons/bad/manifest.json
|
||||
addons/bug1273184.xpi
|
||||
addons/test-devtools-webextension/*
|
||||
|
@ -22,9 +21,6 @@ support-files =
|
|||
!/devtools/client/shared/test/shared-head.js
|
||||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||
|
||||
[browser_addons_debug_bootstrapped.js]
|
||||
# To be removed in bug 1497264
|
||||
skip-if = true
|
||||
[browser_addons_debug_info.js]
|
||||
[browser_addons_debug_webextension.js]
|
||||
tags = webextensions
|
||||
|
@ -36,16 +32,11 @@ tags = webextensions
|
|||
skip-if = (verify && debug) || (debug && os == "linux" && bits == 64) # verify: crashes on shutdown, timeouts linux debug Bug 1299001
|
||||
tags = webextensions
|
||||
[browser_addons_debugging_initial_state.js]
|
||||
# To be removed or updated in bug 1497264
|
||||
skip-if = true
|
||||
[browser_addons_install.js]
|
||||
# To be updated in bug 1497264 (was "verify && debug")
|
||||
skip-if = true
|
||||
skip-if = verify && debug
|
||||
[browser_addons_reload.js]
|
||||
[browser_addons_remove.js]
|
||||
[browser_addons_toggle_debug.js]
|
||||
# To be removed or updated in bug 1497264
|
||||
skip-if = true
|
||||
[browser_page_not_found.js]
|
||||
[browser_service_workers.js]
|
||||
[browser_service_workers_fetch_flag.js]
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// There are shutdown issues for which multiple rejections are left uncaught.
|
||||
// See bug 1018184 for resolving these issues.
|
||||
const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
|
||||
PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
|
||||
|
||||
// Avoid test timeouts that can occur while waiting for the "addon-console-works" message.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const ADDON_ID = "test-devtools@mozilla.org";
|
||||
const ADDON_NAME = "test-devtools";
|
||||
|
||||
const { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
|
||||
|
||||
add_task(async function() {
|
||||
await new Promise(resolve => {
|
||||
const options = {"set": [
|
||||
// Force enabling of addons debugging
|
||||
["devtools.chrome.enabled", true],
|
||||
["devtools.debugger.remote-enabled", true],
|
||||
// Disable security prompt
|
||||
["devtools.debugger.prompt-connection", false],
|
||||
// Enable Browser toolbox test script execution via env variable
|
||||
["devtools.browser-toolbox.allow-unsafe-script", true],
|
||||
]};
|
||||
SpecialPowers.pushPrefEnv(options, resolve);
|
||||
});
|
||||
|
||||
const { tab, document } = await openAboutDebugging("addons");
|
||||
await waitForInitialAddonList(document);
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
name: ADDON_NAME,
|
||||
});
|
||||
|
||||
// Retrieve the DEBUG button for the addon
|
||||
const names = getInstalledAddonNames(document);
|
||||
const name = names.filter(element => element.textContent === ADDON_NAME)[0];
|
||||
ok(name, "Found the addon in the list");
|
||||
const targetElement = name.parentNode.parentNode;
|
||||
const debugBtn = targetElement.querySelector(".debug-button");
|
||||
ok(debugBtn, "Found its debug button");
|
||||
|
||||
// Wait for a notification sent by a script evaluated the test addon via
|
||||
// the web console.
|
||||
const onCustomMessage = new Promise(done => {
|
||||
Services.obs.addObserver(function listener() {
|
||||
Services.obs.removeObserver(listener, "addon-console-works");
|
||||
done();
|
||||
}, "addon-console-works");
|
||||
});
|
||||
|
||||
// Be careful, this JS function is going to be executed in the addon toolbox,
|
||||
// which lives in another process. So do not try to use any scope variable!
|
||||
const env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
const testScript = function() {
|
||||
/* eslint-disable no-undef */
|
||||
toolbox.selectTool("webconsole")
|
||||
.then(console => {
|
||||
const { jsterm } = console.hud;
|
||||
return jsterm.execute("myBootstrapAddonFunction()");
|
||||
})
|
||||
.then(() => toolbox.destroy());
|
||||
/* eslint-enable no-undef */
|
||||
};
|
||||
env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
|
||||
registerCleanupFunction(() => {
|
||||
env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
|
||||
});
|
||||
|
||||
const onToolboxClose = BrowserToolboxProcess.once("close");
|
||||
|
||||
debugBtn.click();
|
||||
|
||||
await onCustomMessage;
|
||||
ok(true, "Received the notification message from the bootstrap.js function");
|
||||
|
||||
await onToolboxClose;
|
||||
ok(true, "Addon toolbox closed");
|
||||
|
||||
await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
|
||||
await closeAboutDebugging(tab);
|
||||
});
|
|
@ -13,29 +13,6 @@ function testFilePath(container, expectedFilePath) {
|
|||
is(filePath.previousElementSibling.textContent, "Location", "file path has label");
|
||||
}
|
||||
|
||||
// Remove in Bug 1497264
|
||||
/*
|
||||
add_task(async function testLegacyAddon() {
|
||||
const addonId = "test-devtools@mozilla.org";
|
||||
const addonName = "test-devtools";
|
||||
const { tab, document } = await openAboutDebugging("addons");
|
||||
await waitForInitialAddonList(document);
|
||||
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
name: addonName,
|
||||
});
|
||||
|
||||
const container = document.querySelector(`[data-addon-id="${addonId}"]`);
|
||||
testFilePath(container, "browser/devtools/client/aboutdebugging/test/addons/unpacked/");
|
||||
|
||||
await uninstallAddon({document, id: addonId, name: addonName});
|
||||
|
||||
await closeAboutDebugging(tab);
|
||||
});
|
||||
*/
|
||||
|
||||
add_task(async function testWebExtension() {
|
||||
const addonId = "test-devtools-webextension-nobg@mozilla.org";
|
||||
const addonName = "test-devtools-webextension-nobg";
|
||||
|
@ -57,7 +34,6 @@ add_task(async function testWebExtension() {
|
|||
document,
|
||||
file: addonFile,
|
||||
name: addonName,
|
||||
isWebExtension: true,
|
||||
});
|
||||
|
||||
const container = document.querySelector(`[data-addon-id="${addonId}"]`);
|
||||
|
@ -95,7 +71,6 @@ add_task(async function testTemporaryWebExtension() {
|
|||
document,
|
||||
file: addonFile,
|
||||
name: addonName,
|
||||
isWebExtension: true,
|
||||
});
|
||||
|
||||
const addons =
|
||||
|
@ -138,7 +113,6 @@ add_task(async function testUnknownManifestProperty() {
|
|||
document,
|
||||
file: addonFile,
|
||||
name: addonName,
|
||||
isWebExtension: true,
|
||||
});
|
||||
|
||||
info("Wait until the addon appears in about:debugging");
|
||||
|
|
|
@ -52,7 +52,7 @@ async function testCheckboxState(testData) {
|
|||
info("Install a test addon.");
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
path: "addons/unpacked/manifest.json",
|
||||
name: ADDON_NAME,
|
||||
});
|
||||
|
||||
|
|
|
@ -31,22 +31,23 @@ function promiseWriteWebManifestForExtension(manifest, dir) {
|
|||
dir.path, manifest.applications.gecko.id, files, true);
|
||||
}
|
||||
|
||||
add_task(async function testLegacyInstallSuccess() {
|
||||
const ADDON_ID = "test-devtools@mozilla.org";
|
||||
const ADDON_NAME = "test-devtools";
|
||||
|
||||
add_task(async function testWebextensionInstallSuccess() {
|
||||
const { tab, document } = await openAboutDebugging("addons");
|
||||
await waitForInitialAddonList(document);
|
||||
|
||||
// Install this add-on, and verify that it appears in the about:debugging UI
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
name: ADDON_NAME,
|
||||
path: "addons/unpacked/manifest.json",
|
||||
name: "test-devtools",
|
||||
});
|
||||
|
||||
// Install the add-on, and verify that it disappears in the about:debugging UI
|
||||
await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
|
||||
await uninstallAddon({
|
||||
document,
|
||||
id: "test-devtools@mozilla.org",
|
||||
name: "test-devtools",
|
||||
});
|
||||
|
||||
await closeAboutDebugging(tab);
|
||||
});
|
||||
|
|
|
@ -68,7 +68,7 @@ add_task(async function reloadButtonReloadsAddon() {
|
|||
await waitForInitialAddonList(document);
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
path: "addons/unpacked/manifest.json",
|
||||
name: ADDON_NAME,
|
||||
});
|
||||
|
||||
|
@ -76,16 +76,13 @@ add_task(async function reloadButtonReloadsAddon() {
|
|||
is(reloadButton.title, "", "Reload button should not have a tooltip");
|
||||
const onInstalled = promiseAddonEvent("onInstalled");
|
||||
|
||||
const onBootstrapInstallCalled = new Promise(done => {
|
||||
Services.obs.addObserver(function listener() {
|
||||
Services.obs.removeObserver(listener, ADDON_NAME);
|
||||
info("Add-on was re-installed: " + ADDON_NAME);
|
||||
done();
|
||||
}, ADDON_NAME);
|
||||
});
|
||||
|
||||
const reloaded = once(AboutDebugging, "addon-reload");
|
||||
const onListUpdated = once(AboutDebugging, "addons-updated");
|
||||
|
||||
// The list is updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event
|
||||
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
|
||||
reloadButton.click();
|
||||
await reloaded;
|
||||
await onListUpdated;
|
||||
|
@ -94,7 +91,6 @@ add_task(async function reloadButtonReloadsAddon() {
|
|||
is(reloadedAddon.name, ADDON_NAME,
|
||||
"Add-on was reloaded: " + reloadedAddon.name);
|
||||
|
||||
await onBootstrapInstallCalled;
|
||||
await tearDownAddon(AboutDebugging, reloadedAddon);
|
||||
await closeAboutDebugging(tab);
|
||||
});
|
||||
|
@ -119,8 +115,9 @@ add_task(async function reloadButtonRefreshesMetadata() {
|
|||
const tempExt = new TempWebExt(ADDON_ID);
|
||||
tempExt.writeManifest(manifestBase);
|
||||
|
||||
// The list is updated twice. On AddonManager's onInstalled event as well
|
||||
// as WebExtension's Management's startup event.
|
||||
// List updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event.
|
||||
let onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
const onInstalled = promiseAddonEvent("onInstalled");
|
||||
await AddonManager.installTemporaryAddon(tempExt.sourceDir);
|
||||
|
@ -135,8 +132,9 @@ add_task(async function reloadButtonRefreshesMetadata() {
|
|||
const newName = "Temporary web extension (updated)";
|
||||
tempExt.writeManifest(Object.assign({}, manifestBase, {name: newName}));
|
||||
|
||||
// The list is updated twice, once for uninstall of the old
|
||||
// and another one for install of the new
|
||||
// List updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event.
|
||||
onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
// Wait for the add-on list to be updated with the reloaded name.
|
||||
const onReInstall = promiseAddonEvent("onInstalled");
|
||||
|
@ -162,8 +160,9 @@ add_task(async function onlyTempInstalledAddonsCanBeReloaded() {
|
|||
const { AboutDebugging } = window;
|
||||
await waitForInitialAddonList(document);
|
||||
|
||||
// The list is updated twice. On AddonManager's onInstalled event as well
|
||||
// as WebExtension's Management's startup event.
|
||||
// List updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event.
|
||||
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
|
||||
await onListUpdated;
|
||||
|
|
|
@ -12,34 +12,6 @@ function getRemoveButton(document, id) {
|
|||
return document.querySelector(`[data-addon-id="${id}"] .uninstall-button`);
|
||||
}
|
||||
|
||||
// Remove in Bug 1497264
|
||||
/*
|
||||
add_task(async function removeLegacyExtension() {
|
||||
const addonID = "test-devtools@mozilla.org";
|
||||
const addonName = "test-devtools";
|
||||
|
||||
const { tab, document } = await openAboutDebugging("addons");
|
||||
await waitForInitialAddonList(document);
|
||||
|
||||
// Install this add-on, and verify that it appears in the about:debugging UI
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
name: addonName,
|
||||
});
|
||||
|
||||
ok(getTargetEl(document, addonID), "add-on is shown");
|
||||
|
||||
info("Click on the remove button and wait until the addon container is removed");
|
||||
getRemoveButton(document, addonID).click();
|
||||
await waitUntil(() => !getTargetEl(document, addonID), 100);
|
||||
|
||||
info("add-on is not shown");
|
||||
|
||||
await closeAboutDebugging(tab);
|
||||
});
|
||||
*/
|
||||
|
||||
add_task(async function removeWebextension() {
|
||||
const addonID = "test-devtools-webextension@mozilla.org";
|
||||
const addonName = "test-devtools-webextension";
|
||||
|
@ -62,7 +34,6 @@ add_task(async function removeWebextension() {
|
|||
document,
|
||||
file: addonFile,
|
||||
name: addonName,
|
||||
isWebExtension: true,
|
||||
});
|
||||
|
||||
ok(getTargetEl(document, addonID), "add-on is shown");
|
||||
|
@ -81,8 +52,9 @@ add_task(async function onlyTempInstalledAddonsCanBeRemoved() {
|
|||
const { AboutDebugging } = window;
|
||||
await waitForInitialAddonList(document);
|
||||
|
||||
// The list is updated twice. On AddonManager's onInstalled event as well
|
||||
// as WebExtension's Management's startup event.
|
||||
// List updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event.
|
||||
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
|
||||
await onListUpdated;
|
||||
|
|
|
@ -25,7 +25,7 @@ add_task(async function() {
|
|||
info("Install a test addon.");
|
||||
await installAddon({
|
||||
document,
|
||||
path: "addons/unpacked/install.rdf",
|
||||
path: "addons/unpacked/manifest.json",
|
||||
name: ADDON_NAME,
|
||||
});
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ function getTabList(document) {
|
|||
document.querySelector("#tabs.targets");
|
||||
}
|
||||
|
||||
async function installAddon({document, path, file, name, isWebExtension}) {
|
||||
async function installAddon({document, path, file, name}) {
|
||||
// Mock the file picker to select a test addon
|
||||
const MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
|
@ -177,32 +177,27 @@ async function installAddon({document, path, file, name, isWebExtension}) {
|
|||
MockFilePicker.setFiles([file]);
|
||||
}
|
||||
|
||||
let onAddonInstalled;
|
||||
const onAddonInstalled = new Promise(done => {
|
||||
Management.on("startup", function listener(event, extension) {
|
||||
if (extension.name != name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWebExtension) {
|
||||
onAddonInstalled = new Promise(done => {
|
||||
Management.on("startup", function listener(event, extension) {
|
||||
if (extension.name != name) {
|
||||
return;
|
||||
}
|
||||
|
||||
Management.off("startup", listener);
|
||||
done();
|
||||
});
|
||||
Management.off("startup", listener);
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
// Wait for a "test-devtools" message sent by the addon's bootstrap.js file
|
||||
onAddonInstalled = new Promise(done => {
|
||||
Services.obs.addObserver(function listener() {
|
||||
Services.obs.removeObserver(listener, "test-devtools");
|
||||
});
|
||||
const AboutDebugging = document.defaultView.AboutDebugging;
|
||||
|
||||
// List updated twice:
|
||||
// - AddonManager's onInstalled event
|
||||
// - WebExtension's Management's startup event.
|
||||
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
|
||||
|
||||
done();
|
||||
}, "test-devtools");
|
||||
});
|
||||
}
|
||||
// Trigger the file picker by clicking on the button
|
||||
document.getElementById("load-addon-from-file").click();
|
||||
|
||||
await onListUpdated;
|
||||
await onAddonInstalled;
|
||||
ok(true, "Addon installed and running its bootstrap.js file");
|
||||
|
||||
|
@ -367,7 +362,6 @@ async function setupTestAboutDebuggingWebExtension(name, file) {
|
|||
document,
|
||||
file,
|
||||
name,
|
||||
isWebExtension: true,
|
||||
});
|
||||
|
||||
// Retrieve the DEBUG button for the addon
|
||||
|
|
|
@ -52,8 +52,11 @@ class AccessibilityStartup {
|
|||
await this.target.actorHasMethod("accessibility", "enable");
|
||||
|
||||
if (this._supports.enableDisable) {
|
||||
this._supports.relations =
|
||||
await this.target.actorHasMethod("accessible", "getRelations");
|
||||
([ this._supports.relations, this._supports.snapshot ] = await Promise.all([
|
||||
this.target.actorHasMethod("accessible", "getRelations"),
|
||||
this.target.actorHasMethod("accessible", "snapshot"),
|
||||
]));
|
||||
|
||||
await this._accessibility.bootstrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
/* global gToolbox, EVENTS */
|
||||
/* global gTelemetry, gToolbox, EVENTS */
|
||||
|
||||
// React & Redux
|
||||
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
|
||||
|
@ -16,12 +16,25 @@ const TreeRow = require("devtools/client/shared/components/tree/TreeRow");
|
|||
// Utils
|
||||
const {flashElementOn, flashElementOff} =
|
||||
require("devtools/client/inspector/markup/utils");
|
||||
const { openDocLink } = require("devtools/client/shared/link");
|
||||
const { VALUE_FLASHING_DURATION, VALUE_HIGHLIGHT_DURATION } = require("../constants");
|
||||
|
||||
// Actions
|
||||
const { updateDetails } = require("../actions/details");
|
||||
const { unhighlight } = require("../actions/accessibles");
|
||||
|
||||
const { L10N } = require("../utils/l10n");
|
||||
|
||||
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
|
||||
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
|
||||
|
||||
const JSON_URL_PREFIX = "data:application/json;charset=UTF-8,";
|
||||
|
||||
const TELEMETRY_ACCESSIBLE_CONTEXT_MENU_OPENED =
|
||||
"devtools.accessibility.accessible_context_menu_opened";
|
||||
const TELEMETRY_ACCESSIBLE_CONTEXT_MENU_ITEM_ACTIVATED =
|
||||
"devtools.accessibility.accessible_context_menu_item_activated";
|
||||
|
||||
class HighlightableTreeRowClass extends TreeRow {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const props = ["name", "open", "value", "loading", "selected", "hasChildren"];
|
||||
|
@ -138,6 +151,53 @@ class AccessibilityRow extends Component {
|
|||
walker.unhighlight().catch(error => console.warn(error));
|
||||
}
|
||||
|
||||
async printToJSON() {
|
||||
const { member, supports } = this.props;
|
||||
if (!supports.snapshot) {
|
||||
// Debugger server does not support Accessible actor snapshots.
|
||||
return;
|
||||
}
|
||||
|
||||
if (gTelemetry) {
|
||||
gTelemetry.keyedScalarAdd(TELEMETRY_ACCESSIBLE_CONTEXT_MENU_ITEM_ACTIVATED,
|
||||
"print-to-json", 1);
|
||||
}
|
||||
|
||||
const snapshot = await member.object.snapshot();
|
||||
openDocLink(`${JSON_URL_PREFIX}${encodeURIComponent(JSON.stringify(snapshot))}`);
|
||||
}
|
||||
|
||||
onContextMenu(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!gToolbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
const menu = new Menu({ id: "accessibility-row-contextmenu" });
|
||||
const { supports } = this.props;
|
||||
|
||||
if (supports.snapshot) {
|
||||
menu.append(new MenuItem({
|
||||
id: "menu-printtojson",
|
||||
label: L10N.getStr("accessibility.tree.menu.printToJSON"),
|
||||
click: () => this.printToJSON(),
|
||||
}));
|
||||
}
|
||||
|
||||
menu.popup(e.screenX, e.screenY, gToolbox);
|
||||
|
||||
if (gTelemetry) {
|
||||
gTelemetry.scalarAdd(TELEMETRY_ACCESSIBLE_CONTEXT_MENU_OPENED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
get hasContextMenu() {
|
||||
const { supports } = this.props;
|
||||
return supports.snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render accessible row component.
|
||||
* @returns acecssible-row React component.
|
||||
|
@ -145,6 +205,7 @@ class AccessibilityRow extends Component {
|
|||
render() {
|
||||
const { object } = this.props.member;
|
||||
const props = Object.assign({}, this.props, {
|
||||
onContextMenu: this.hasContextMenu && (e => this.onContextMenu(e)),
|
||||
onMouseOver: () => this.highlight(object),
|
||||
onMouseOut: () => this.unhighlight(),
|
||||
});
|
||||
|
|
|
@ -240,8 +240,11 @@ AccessibilityPanel.prototype = {
|
|||
this.panelWin.off(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
|
||||
this.onAccessibilityInspectorUpdated);
|
||||
|
||||
this.picker.release();
|
||||
this.picker = null;
|
||||
// Older versions of debugger server do not support picker functionality.
|
||||
if (this.picker) {
|
||||
this.picker.release();
|
||||
this.picker = null;
|
||||
}
|
||||
|
||||
if (this.front) {
|
||||
this.front.off("init", this.updateA11YServiceDurationTimer);
|
||||
|
|
|
@ -5,3 +5,4 @@ support-files =
|
|||
[test_accessible_learnMoreLink.html]
|
||||
[test_accessible_openLink.html]
|
||||
[test_accessible_relations.html]
|
||||
[test_accessible_row_context_menu.html]
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
<!-- 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/. -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that openLink function is called if accessible object property is rendered as a link.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AccessibilityRow context menu test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
window.onload = async function() {
|
||||
try {
|
||||
const { gDevTools } = require("devtools/client/framework/devtools");
|
||||
const Services = browserRequire("Services");
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const { createFactory, createElement } =
|
||||
browserRequire("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
const createStore = require("devtools/client/shared/redux/create-store")();
|
||||
const { Simulate } =
|
||||
browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
|
||||
const AccessibilityRow = createFactory(
|
||||
browserRequire("devtools/client/accessibility/components/AccessibilityRow"));
|
||||
|
||||
async function withMockEnv(func) {
|
||||
const { gToolbox: originalToolbox, gTelemetry: originalTelemetry } = window;
|
||||
window.gToolbox = { doc: document };
|
||||
window.gTelemetry = null;
|
||||
|
||||
await func();
|
||||
|
||||
window.gToolbox = originalToolbox;
|
||||
window.gTelemetry = originalTelemetry;
|
||||
}
|
||||
|
||||
function renderAccessibilityRow(newProps, newState) {
|
||||
let container = document.getElementById("container");
|
||||
if (container) {
|
||||
container.remove();
|
||||
}
|
||||
|
||||
const accRow = AccessibilityRow(newProps);
|
||||
const mockStore = createStore((state, action) =>
|
||||
action ? { ...state, ...action } : state, newState);
|
||||
const provider = createElement(Provider, { store: mockStore }, accRow);
|
||||
|
||||
container = document.createElement("div");
|
||||
container.id = "container";
|
||||
document.body.appendChild(container);
|
||||
return ReactDOM.render(provider, container);
|
||||
}
|
||||
|
||||
const checker = Symbol();
|
||||
let isCalled;
|
||||
|
||||
const ROW_ID = "test-row";
|
||||
const JSON_URL_PREFIX = "data:application/json;charset=UTF-8,";
|
||||
const SNAPSHOT = { "snapshot": true };
|
||||
const defaultProps = {
|
||||
id: ROW_ID,
|
||||
member: {
|
||||
object: {
|
||||
name: "test",
|
||||
value: "test",
|
||||
loading: false,
|
||||
selected: false,
|
||||
hasChildren: false,
|
||||
snapshot: async () => SNAPSHOT,
|
||||
},
|
||||
},
|
||||
columns: [
|
||||
{ "id": "default", "title": "role" },
|
||||
{ "id": "value", "title": "name" },
|
||||
],
|
||||
provider: {
|
||||
getValue: (object, id) => object[id],
|
||||
},
|
||||
};
|
||||
|
||||
const mockProps = {
|
||||
...defaultProps,
|
||||
onContextMenu: () => {
|
||||
isCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const defaultState = { ui: { supports: { snapshot: true }}};
|
||||
const mockState = { ui: { supports: {}}};
|
||||
|
||||
info("Check contextmenu default behaviour.");
|
||||
renderAccessibilityRow(defaultProps, defaultState);
|
||||
let row = document.getElementById(ROW_ID);
|
||||
|
||||
await withMockEnv(async function() {
|
||||
Simulate.contextMenu(row);
|
||||
|
||||
const menu = document.getElementById("accessibility-row-contextmenu");
|
||||
const printtojsonMenuItem = document.getElementById("menu-printtojson");
|
||||
|
||||
ok(menu, "Accessibility row context menu is open");
|
||||
ok(printtojsonMenuItem, "Print to JSON menu item is visible");
|
||||
|
||||
const browserWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
const defaultOpenWebLinkIn = browserWindow.openWebLinkIn;
|
||||
|
||||
let openWebLinkInCalled;
|
||||
const openWebLinkInPromise = new Promise(resolve => {
|
||||
openWebLinkInCalled = resolve;
|
||||
});
|
||||
|
||||
// Mock top chrome window's @openWebLinkIn method.
|
||||
browserWindow.openWebLinkIn = (...args) => {
|
||||
openWebLinkInCalled(args);
|
||||
};
|
||||
printtojsonMenuItem.click();
|
||||
|
||||
const [ url, where ] = await openWebLinkInPromise;
|
||||
is(url, `${JSON_URL_PREFIX}${encodeURIComponent(JSON.stringify(SNAPSHOT))}`,
|
||||
"Correct URL is opened");
|
||||
is(where, "tab", "URL was opened correctly");
|
||||
|
||||
// Reset @openWebLinkIn to default.
|
||||
browserWindow.openWebLinkIn = defaultOpenWebLinkIn;
|
||||
});
|
||||
|
||||
info("Check accessibility row when context menu is not supported.");
|
||||
renderAccessibilityRow(mockProps, mockState);
|
||||
row = document.getElementById(ROW_ID);
|
||||
|
||||
info("Check contextmenu listener is not called when context menu is not supported.");
|
||||
isCalled = checker;
|
||||
Simulate.contextMenu(row);
|
||||
ok(isCalled === checker, "contextmenu event handler was never called.");
|
||||
|
||||
info("Check accessibility row when context menu is supported.");
|
||||
renderAccessibilityRow(mockProps, defaultState);
|
||||
row = document.getElementById(ROW_ID);
|
||||
|
||||
info("Check contextmenu listener is called when context menu is supported.");
|
||||
isCalled = checker;
|
||||
Simulate.contextMenu(row);
|
||||
ok(isCalled, "contextmenu event handler was called correctly.");
|
||||
} catch (e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -283,8 +283,8 @@ button.invoke-getter {
|
|||
mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
|
||||
display: inline-block;
|
||||
background-color: var(--comment-node-color);
|
||||
height: 12px;
|
||||
vertical-align:bottom;
|
||||
height: 10px;
|
||||
vertical-align: middle;
|
||||
border:none;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,3 +101,8 @@ accessibility.description.general.p2=Accessibility features may affect the perfo
|
|||
# when accessibility service description is provided when a client is connected
|
||||
# to an older version of accessibility actor.
|
||||
accessibility.description.oldVersion=You are connected to a debugger server that is too old. To use Accessibility panel, please connect to the latest debugger server version.
|
||||
|
||||
# LOCALIZATION NOTE (accessibility.tree.menu.printToJSON): A title text used when a
|
||||
# context menu item for printing an accessible tree to JSON is rendered after triggering a
|
||||
# context menu for an accessible tree row.
|
||||
accessibility.tree.menu.printToJSON=Print to JSON
|
||||
|
|
|
@ -283,8 +283,8 @@ button.invoke-getter {
|
|||
mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
|
||||
display: inline-block;
|
||||
background-color: var(--comment-node-color);
|
||||
height: 12px;
|
||||
vertical-align:bottom;
|
||||
height: 10px;
|
||||
vertical-align: middle;
|
||||
border:none;
|
||||
}
|
||||
|
||||
|
@ -436,6 +436,7 @@ html[dir="rtl"] .tree-node button.arrow {
|
|||
}
|
||||
|
||||
.tree-node.focused button.jump-definition,
|
||||
.tree-node.focused button.open-inspector {
|
||||
.tree-node.focused button.open-inspector,
|
||||
.tree-node.focused button.invoke-getter {
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -49,6 +49,7 @@ define(function(require, exports, module) {
|
|||
id: PropTypes.string.isRequired,
|
||||
provider: PropTypes.object.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
onContextMenu: PropTypes.func,
|
||||
onMouseOver: PropTypes.func,
|
||||
onMouseOut: PropTypes.func,
|
||||
};
|
||||
|
@ -120,12 +121,14 @@ define(function(require, exports, module) {
|
|||
render() {
|
||||
const member = this.props.member;
|
||||
const decorator = this.props.decorator;
|
||||
|
||||
const props = {
|
||||
id: this.props.id,
|
||||
role: "treeitem",
|
||||
"aria-level": member.level,
|
||||
"aria-selected": !!member.selected,
|
||||
onClick: this.props.onClick,
|
||||
onContextMenu: this.props.onContextMenu,
|
||||
onMouseOver: this.props.onMouseOver,
|
||||
onMouseOut: this.props.onMouseOut,
|
||||
};
|
||||
|
|
|
@ -723,6 +723,11 @@ function getChartsFromToolId(id) {
|
|||
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
||||
countScalar = `devtools.accessibility.picker_used_count`;
|
||||
break;
|
||||
case "CHANGESVIEW":
|
||||
useTimedEvent = true;
|
||||
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
||||
countScalar = `devtools.${lowerCaseId}.opened_count`;
|
||||
break;
|
||||
case "ANIMATIONINSPECTOR":
|
||||
case "COMPUTEDVIEW":
|
||||
case "FONTINSPECTOR":
|
||||
|
|
Двоичные данные
devtools/client/shared/test/addon1.xpi
Двоичные данные
devtools/client/shared/test/addon1.xpi
Двоичный файл не отображается.
Двоичные данные
devtools/client/shared/test/addon2.xpi
Двоичные данные
devtools/client/shared/test/addon2.xpi
Двоичный файл не отображается.
Двоичные данные
devtools/client/shared/test/addon4.xpi
Двоичные данные
devtools/client/shared/test/addon4.xpi
Двоичный файл не отображается.
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "test-addon-1",
|
||||
"version": "1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "test-addon-1@mozilla.org"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "test-addon-2",
|
||||
"version": "1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "test-addon-2@mozilla.org"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,7 @@
|
|||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
addon1.xpi
|
||||
addon2.xpi
|
||||
addon4.xpi
|
||||
addons/*
|
||||
browser_devices.json
|
||||
code_listworkers-worker1.js
|
||||
code_listworkers-worker2.js
|
||||
|
@ -76,10 +74,6 @@ support-files =
|
|||
[browser_cubic-bezier-05.js]
|
||||
[browser_cubic-bezier-06.js]
|
||||
[browser_cubic-bezier-07.js]
|
||||
[browser_dbg_addon-console.js]
|
||||
# To be removed or updated in bug 1497264
|
||||
# previously was: (e10s && debug || os == 'win' || verify)
|
||||
skip-if = true
|
||||
tags = addons
|
||||
[browser_dbg_debugger-statement.js]
|
||||
skip-if = e10s && debug
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from helper_addons.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_addons.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
var { Task } = require("devtools/shared/task");
|
||||
|
||||
// Test that the we can see console messages from the add-on
|
||||
|
||||
const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
|
||||
const ADDON_PATH = "addon4.xpi";
|
||||
|
||||
function getCachedMessages(webConsole) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
webConsole.getCachedMessages(["ConsoleAPI"], response => {
|
||||
if (response.error) {
|
||||
deferred.reject(response.error);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(response.messages);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Creates an add-on debugger for a given add-on. The returned AddonDebugger
|
||||
// object must be destroyed before finishing the test
|
||||
function initAddonDebugger(addonId) {
|
||||
const addonDebugger = new AddonDebugger();
|
||||
return addonDebugger.init(addonId).then(() => addonDebugger);
|
||||
}
|
||||
|
||||
function AddonDebugger() {
|
||||
this._onMessage = this._onMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AddonDebugger.prototype = {
|
||||
init: Task.async(function* (addonId) {
|
||||
info("Initializing an addon debugger panel.");
|
||||
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
this.frame = document.createElement("iframe");
|
||||
this.frame.setAttribute("height", 400);
|
||||
document.documentElement.appendChild(this.frame);
|
||||
window.addEventListener("message", this._onMessage);
|
||||
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
this.client = new DebuggerClient(transport);
|
||||
|
||||
yield this.client.connect();
|
||||
|
||||
const addonTargetFront = yield this.client.mainRoot.getAddon({ id: addonId });
|
||||
|
||||
const targetOptions = {
|
||||
activeTab: addonTargetFront,
|
||||
client: this.client,
|
||||
chrome: true,
|
||||
};
|
||||
|
||||
const toolboxOptions = {
|
||||
customIframe: this.frame,
|
||||
};
|
||||
|
||||
this.target = yield TargetFactory.forRemoteTab(targetOptions);
|
||||
const toolbox = yield gDevTools.showToolbox(
|
||||
this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
|
||||
|
||||
info("Addon debugger panel shown successfully.");
|
||||
|
||||
this.debuggerPanel = toolbox.getCurrentPanel();
|
||||
yield this._attachConsole();
|
||||
}),
|
||||
|
||||
destroy: Task.async(function* () {
|
||||
yield this.client.close();
|
||||
this.frame.remove();
|
||||
window.removeEventListener("message", this._onMessage);
|
||||
}),
|
||||
|
||||
_attachConsole: function() {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"])
|
||||
.then(([aResponse, aWebConsoleClient]) => {
|
||||
this.webConsole = aWebConsoleClient;
|
||||
this.client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
deferred.resolve();
|
||||
}, e => {
|
||||
deferred.reject(e);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_onConsoleAPICall: function(type, packet) {
|
||||
if (packet.from != this.webConsole.actor) {
|
||||
return;
|
||||
}
|
||||
this.emit("console", packet.message);
|
||||
},
|
||||
|
||||
_onMessage: function(event) {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
const msg = event.data;
|
||||
switch (msg.name) {
|
||||
case "toolbox-title":
|
||||
this.title = msg.data.value;
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
add_task(async function test() {
|
||||
const addon = await addTemporaryAddon(ADDON_PATH);
|
||||
const addonDebugger = await initAddonDebugger(ADDON_ID);
|
||||
|
||||
const webConsole = addonDebugger.webConsole;
|
||||
const messages = await getCachedMessages(webConsole);
|
||||
is(messages.length, 1, "Should be one cached message");
|
||||
is(messages[0].arguments[0].type, "object", "Should have logged an object");
|
||||
is(messages[0].arguments[0].preview.ownProperties.msg.value,
|
||||
"Hello from the test add-on", "Should have got the right message");
|
||||
|
||||
const consolePromise = addonDebugger.once("console");
|
||||
|
||||
console.log("Bad message");
|
||||
Services.obs.notifyObservers(null, "addon-test-ping");
|
||||
|
||||
const messageGrip = await consolePromise;
|
||||
is(messageGrip.arguments[0].type, "object", "Should have logged an object");
|
||||
is(messageGrip.arguments[0].preview.ownProperties.msg.value,
|
||||
"Hello again", "Should have got the right message");
|
||||
|
||||
await addonDebugger.destroy();
|
||||
await removeAddon(addon);
|
||||
finish();
|
||||
});
|
|
@ -16,103 +16,46 @@ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
|||
/**
|
||||
* Make sure the listAddons request works as specified.
|
||||
*/
|
||||
const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
|
||||
const ADDON1_PATH = "addon1.xpi";
|
||||
const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
|
||||
const ADDON2_PATH = "addon2.xpi";
|
||||
const ADDON1_ID = "test-addon-1@mozilla.org";
|
||||
const ADDON1_PATH = "addons/test-addon-1/";
|
||||
const ADDON2_ID = "test-addon-2@mozilla.org";
|
||||
const ADDON2_PATH = "addons/test-addon-2/";
|
||||
|
||||
var gAddon1, gAddon1Front, gAddon2, gClient;
|
||||
|
||||
function test() {
|
||||
add_task(async function() {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect().then(([aType, aTraits]) => {
|
||||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
const client = new DebuggerClient(transport);
|
||||
|
||||
promise.resolve(null)
|
||||
.then(testFirstAddon)
|
||||
.then(testSecondAddon)
|
||||
.then(testRemoveFirstAddon)
|
||||
.then(testRemoveSecondAddon)
|
||||
.then(() => gClient.close())
|
||||
.then(finish)
|
||||
.catch(error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
const [type] = await client.connect();
|
||||
is(type, "browser", "Root actor should identify itself as a browser.");
|
||||
|
||||
function testFirstAddon() {
|
||||
let addonListChanged = false;
|
||||
gClient.mainRoot.once("addonListChanged").then(() => {
|
||||
addonListChanged = true;
|
||||
});
|
||||
let addonListChangedEvents = 0;
|
||||
client.mainRoot.on("addonListChanged", () => addonListChangedEvents++);
|
||||
|
||||
return addTemporaryAddon(ADDON1_PATH).then(addon => {
|
||||
gAddon1 = addon;
|
||||
const addon1 = await addTemporaryAddon(ADDON1_PATH);
|
||||
const addonFront1 = await client.mainRoot.getAddon({ id: ADDON1_ID });
|
||||
is(addonListChangedEvents, 0, "Should not receive addonListChanged yet.");
|
||||
ok(addonFront1, "Should find an addon actor for addon1.");
|
||||
|
||||
return gClient.mainRoot.getAddon({ id: ADDON1_ID }).then(front => {
|
||||
ok(!addonListChanged, "Should not yet be notified that list of addons changed.");
|
||||
ok(front, "Should find an addon actor for addon1.");
|
||||
gAddon1Front = front;
|
||||
});
|
||||
});
|
||||
}
|
||||
const addon2 = await addTemporaryAddon(ADDON2_PATH);
|
||||
const front1AfterAddingAddon2 = await client.mainRoot.getAddon({ id: ADDON1_ID });
|
||||
const addonFront2 = await client.mainRoot.getAddon({ id: ADDON2_ID });
|
||||
|
||||
function testSecondAddon() {
|
||||
let addonListChanged = false;
|
||||
gClient.mainRoot.once("addonListChanged").then(() => {
|
||||
addonListChanged = true;
|
||||
});
|
||||
is(addonListChangedEvents, 1, "Should have received an addonListChanged event.");
|
||||
ok(addonFront2, "Should find an addon actor for addon2.");
|
||||
is(addonFront1, front1AfterAddingAddon2, "Front for addon1 should be the same");
|
||||
|
||||
return addTemporaryAddon(ADDON2_PATH).then(addon => {
|
||||
gAddon2 = addon;
|
||||
await removeAddon(addon1);
|
||||
const front1AfterRemove = await client.mainRoot.getAddon({ id: ADDON1_ID });
|
||||
is(addonListChangedEvents, 2, "Should have received an addonListChanged event.");
|
||||
ok(!front1AfterRemove, "Should no longer get a front for addon1");
|
||||
|
||||
return gClient.mainRoot.getAddon({ id: ADDON1_ID }).then(front1 => {
|
||||
return gClient.mainRoot.getAddon({ id: ADDON2_ID }).then(front2 => {
|
||||
ok(addonListChanged, "Should be notified that list of addons changed.");
|
||||
is(front1, gAddon1Front, "First addon's actor shouldn't have changed.");
|
||||
ok(front2, "Should find a addon actor for the second addon.");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
await removeAddon(addon2);
|
||||
const front2AfterRemove = await client.mainRoot.getAddon({ id: ADDON2_ID });
|
||||
is(addonListChangedEvents, 3, "Should have received an addonListChanged event.");
|
||||
ok(!front2AfterRemove, "Should no longer get a front for addon1");
|
||||
|
||||
function testRemoveFirstAddon() {
|
||||
let addonListChanged = false;
|
||||
gClient.mainRoot.once("addonListChanged").then(() => {
|
||||
addonListChanged = true;
|
||||
});
|
||||
|
||||
return removeAddon(gAddon1).then(() => {
|
||||
return gClient.mainRoot.getAddon({ id: ADDON1_ID }).then(front => {
|
||||
ok(addonListChanged, "Should be notified that list of addons changed.");
|
||||
ok(!front, "Shouldn't find a addon actor for the first addon anymore.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testRemoveSecondAddon() {
|
||||
let addonListChanged = false;
|
||||
gClient.mainRoot.once("addonListChanged").then(() => {
|
||||
addonListChanged = true;
|
||||
});
|
||||
|
||||
return removeAddon(gAddon2).then(() => {
|
||||
return gClient.mainRoot.getAddon({ id: ADDON2_ID }).then(front => {
|
||||
ok(addonListChanged, "Should be notified that list of addons changed.");
|
||||
ok(!front, "Shouldn't find a addon actor for the second addon anymore.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gAddon1 = null;
|
||||
gAddon1Front = null;
|
||||
gAddon2 = null;
|
||||
gClient = null;
|
||||
await client.close();
|
||||
});
|
||||
|
|
|
@ -20,6 +20,17 @@ const DATA = [
|
|||
value: null,
|
||||
extra: {
|
||||
oldpanel: "layoutview",
|
||||
newpanel: "changesview",
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamp: null,
|
||||
category: "devtools.main",
|
||||
method: "sidepanel_changed",
|
||||
object: "inspector",
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "changesview",
|
||||
newpanel: "animationinspector",
|
||||
},
|
||||
},
|
||||
|
@ -64,6 +75,17 @@ const DATA = [
|
|||
value: null,
|
||||
extra: {
|
||||
oldpanel: "computedview",
|
||||
newpanel: "changesview",
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamp: null,
|
||||
category: "devtools.main",
|
||||
method: "sidepanel_changed",
|
||||
object: "inspector",
|
||||
value: null,
|
||||
extra: {
|
||||
oldpanel: "changesview",
|
||||
newpanel: "animationinspector",
|
||||
},
|
||||
},
|
||||
|
@ -106,6 +128,9 @@ add_task(async function() {
|
|||
// Let's reset the counts.
|
||||
Services.telemetry.clearEvents();
|
||||
|
||||
// Ensure the Changes panel is enabled before running the tests.
|
||||
await pushPref("devtools.inspector.changes.enabled", true);
|
||||
|
||||
// Ensure no events have been logged
|
||||
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
|
||||
ok(!snapshot.parent, "No events have been logged for the main process");
|
||||
|
@ -130,7 +155,7 @@ function testSidebar(toolbox) {
|
|||
|
||||
const inspector = toolbox.getCurrentPanel();
|
||||
let sidebarTools = ["computedview", "layoutview", "fontinspector",
|
||||
"animationinspector"];
|
||||
"animationinspector", "changesview"];
|
||||
|
||||
// Concatenate the array with itself so that we can open each tool twice.
|
||||
sidebarTools = [...sidebarTools, ...sidebarTools];
|
||||
|
@ -159,9 +184,11 @@ function checkResults() {
|
|||
checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
|
||||
checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", {0: 3, 1: 0}, "array");
|
||||
checkTelemetry("DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", "", {0: 2, 1: 0}, "array");
|
||||
checkTelemetry("devtools.changesview.opened_count", "", 2, "scalar");
|
||||
checkTelemetry("DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
checkTelemetry("DEVTOOLS_CHANGESVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
|
||||
}
|
||||
|
||||
function checkEventTelemetry() {
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const { Ci, Cu } = require("chrome");
|
||||
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
|
||||
const { accessibleSpec } = require("devtools/shared/specs/accessibility");
|
||||
|
||||
loader.lazyRequireGetter(this, "getContrastRatioFor", "devtools/server/actors/utils/accessibility", true);
|
||||
loader.lazyRequireGetter(this, "isDefunct", "devtools/server/actors/utils/accessibility", true);
|
||||
loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
|
||||
|
||||
const nsIAccessibleRelation = Ci.nsIAccessibleRelation;
|
||||
const RELATIONS_TO_IGNORE = new Set([
|
||||
|
@ -20,6 +21,92 @@ const RELATIONS_TO_IGNORE = new Set([
|
|||
nsIAccessibleRelation.RELATION_SUBWINDOW_OF,
|
||||
]);
|
||||
|
||||
const STATE_DEFUNCT = Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
|
||||
const CSS_TEXT_SELECTOR = "#text";
|
||||
|
||||
/**
|
||||
* Get node inforamtion such as nodeType and the unique CSS selector for the node.
|
||||
* @param {DOMNode} node
|
||||
* Node for which to get the information.
|
||||
* @return {Object}
|
||||
* Information about the type of the node and how to locate it.
|
||||
*/
|
||||
function getNodeDescription(node) {
|
||||
if (!node || Cu.isDeadWrapper(node)) {
|
||||
return { nodeType: undefined, nodeCssSelector: "" };
|
||||
}
|
||||
|
||||
const { nodeType } = node;
|
||||
return {
|
||||
nodeType,
|
||||
// If node is a text node, we find a unique CSS selector for its parent and add a
|
||||
// CSS_TEXT_SELECTOR postfix to indicate that it's a text node.
|
||||
nodeCssSelector: nodeType === Node.TEXT_NODE ?
|
||||
`${findCssSelector(node.parentNode)}${CSS_TEXT_SELECTOR}` :
|
||||
findCssSelector(node),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a snapshot of the nsIAccessible object including its subtree. None of the subtree
|
||||
* queried here is cached via accessible walker's refMap.
|
||||
* @param {nsIAccessible} acc
|
||||
* Accessible object to take a snapshot of.
|
||||
* @param {nsIAccessibilityService} a11yService
|
||||
* Accessibility service instance in the current process, used to get localized
|
||||
* string representation of various accessible properties.
|
||||
* @return {JSON}
|
||||
* JSON snapshot of the accessibility tree with root at current accessible.
|
||||
*/
|
||||
function getSnapshot(acc, a11yService) {
|
||||
if (isDefunct(acc)) {
|
||||
return {
|
||||
states: [ a11yService.getStringStates(0, STATE_DEFUNCT) ],
|
||||
};
|
||||
}
|
||||
|
||||
const actions = [];
|
||||
for (let i = 0; i < acc.actionCount; i++) {
|
||||
actions.push(acc.getActionDescription(i));
|
||||
}
|
||||
|
||||
const attributes = {};
|
||||
if (acc.attributes) {
|
||||
for (const { key, value } of acc.attributes.enumerate()) {
|
||||
attributes[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const state = {};
|
||||
const extState = {};
|
||||
acc.getState(state, extState);
|
||||
const states = [
|
||||
...a11yService.getStringStates(state.value, extState.value),
|
||||
];
|
||||
|
||||
const children = [];
|
||||
for (let child = acc.firstChild; child; child = child.nextSibling) {
|
||||
children.push(getSnapshot(child, a11yService));
|
||||
}
|
||||
|
||||
const { nodeType, nodeCssSelector } = getNodeDescription(acc.DOMNode);
|
||||
return {
|
||||
name: acc.name,
|
||||
role: a11yService.getStringRole(acc.role),
|
||||
actions,
|
||||
value: acc.value,
|
||||
nodeCssSelector,
|
||||
nodeType,
|
||||
description: acc.description,
|
||||
keyboardShortcut: acc.accessKey || acc.keyboardShortcut,
|
||||
childCount: acc.childCount,
|
||||
indexInParent: acc.indexInParent,
|
||||
states,
|
||||
children,
|
||||
attributes,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The AccessibleActor provides information about a given accessible object: its
|
||||
* role, name, states, etc.
|
||||
|
@ -316,6 +403,10 @@ const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
|
|||
contrastRatio: this._getContrastRatio(),
|
||||
};
|
||||
},
|
||||
|
||||
snapshot() {
|
||||
return getSnapshot(this.rawAccessible, this.walker.a11yService);
|
||||
},
|
||||
});
|
||||
|
||||
exports.AccessibleActor = AccessibleActor;
|
||||
|
|
|
@ -66,6 +66,42 @@ add_task(async function() {
|
|||
is(relations[1].targets[0], docAccessibleFront,
|
||||
"Label's containing document is a root document");
|
||||
|
||||
info("Snapshot");
|
||||
const snapshot = await controlAccessibleFront.snapshot();
|
||||
Assert.deepEqual(snapshot, {
|
||||
"name": "Label",
|
||||
"role": "entry",
|
||||
"actions": [ "Activate" ],
|
||||
"value": "",
|
||||
"nodeCssSelector": "#control",
|
||||
"nodeType": 1,
|
||||
"description": "",
|
||||
"keyboardShortcut": "",
|
||||
"childCount": 0,
|
||||
"indexInParent": 1,
|
||||
"states": [
|
||||
"focusable",
|
||||
"autocompletion",
|
||||
"editable",
|
||||
"opaque",
|
||||
"single line",
|
||||
"enabled",
|
||||
"sensitive",
|
||||
],
|
||||
"children": [],
|
||||
"attributes": {
|
||||
"margin-left": "0px",
|
||||
"text-align": "start",
|
||||
"text-indent": "0px",
|
||||
"id": "control",
|
||||
"tag": "input",
|
||||
"margin-top": "0px",
|
||||
"margin-bottom": "0px",
|
||||
"margin-right": "0px",
|
||||
"display": "inline",
|
||||
"explicit-name": "true",
|
||||
}});
|
||||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
|
|
|
@ -92,6 +92,12 @@ const accessibleSpec = generateActorSpec({
|
|||
relations: RetVal("array:accessibleRelation"),
|
||||
},
|
||||
},
|
||||
snapshot: {
|
||||
request: {},
|
||||
response: {
|
||||
snapshot: RetVal("json"),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/**
|
||||
* Default values for the nsViewportInfo class.
|
||||
*/
|
||||
static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.1f);
|
||||
static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.25f);
|
||||
static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
|
||||
static const mozilla::CSSIntSize kViewportMinSize(200, 40);
|
||||
static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
|
||||
|
|
|
@ -21,13 +21,13 @@
|
|||
await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
|
||||
|
||||
let info = getViewportInfo(800, 480);
|
||||
fuzzeq(info.defaultZoom, 0.1, "initial scale is unspecified");
|
||||
fuzzeq(info.minZoom, 0.1, "minimum scale defaults to the absolute minimum");
|
||||
is(info.maxZoom, 10, "maximum scale defaults to the absolute maximum");
|
||||
is(info.width, 980, "width is the default width");
|
||||
is(info.height, 588, "height is proportional to displayHeight");
|
||||
is(info.autoSize, false, "autoSize is disabled by default");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
fuzzeq(info.defaultZoom, 0.25, "initial scale is clamped to the default mimumim scale");
|
||||
fuzzeq(info.minZoom, 0.25, "minimum scale defaults to the absolute minimum");
|
||||
is(info.maxZoom, 10, "maximum scale defaults to the absolute maximum");
|
||||
is(info.width, 980, "width is the default width");
|
||||
is(info.height, 588, "height is proportional to displayHeight");
|
||||
is(info.autoSize, false, "autoSize is disabled by default");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(490, 600);
|
||||
is(info.width, 980, "width is still the default width");
|
||||
|
|
|
@ -45,8 +45,8 @@ var ecmaGlobals =
|
|||
{name: "ArrayBuffer", insecureContext: true},
|
||||
{name: "Atomics", insecureContext: true, disabled: true},
|
||||
{name: "Boolean", insecureContext: true},
|
||||
{name: "ByteLengthQueuingStrategy", insecureContext: true, nightly: true},
|
||||
{name: "CountQueuingStrategy", insecureContext: true, nightly: true},
|
||||
{name: "ByteLengthQueuingStrategy", insecureContext: true},
|
||||
{name: "CountQueuingStrategy", insecureContext: true},
|
||||
{name: "DataView", insecureContext: true},
|
||||
{name: "Date", insecureContext: true},
|
||||
{name: "Error", insecureContext: true},
|
||||
|
@ -69,7 +69,7 @@ var ecmaGlobals =
|
|||
{name: "Promise", insecureContext: true},
|
||||
{name: "Proxy", insecureContext: true},
|
||||
{name: "RangeError", insecureContext: true},
|
||||
{name: "ReadableStream", insecureContext: true, nightly: true},
|
||||
{name: "ReadableStream", insecureContext: true},
|
||||
{name: "ReferenceError", insecureContext: true},
|
||||
{name: "Reflect", insecureContext: true},
|
||||
{name: "RegExp", insecureContext: true},
|
||||
|
|
|
@ -33,8 +33,8 @@ var ecmaGlobals =
|
|||
{name: "ArrayBuffer", insecureContext: true},
|
||||
{name: "Atomics", insecureContext: true, disabled: true},
|
||||
{name: "Boolean", insecureContext: true},
|
||||
{name: "ByteLengthQueuingStrategy", insecureContext: true, nightly: true},
|
||||
{name: "CountQueuingStrategy", insecureContext: true, nightly: true},
|
||||
{name: "ByteLengthQueuingStrategy", insecureContext: true},
|
||||
{name: "CountQueuingStrategy", insecureContext: true},
|
||||
{name: "DataView", insecureContext: true},
|
||||
{name: "Date", insecureContext: true},
|
||||
{name: "Error", insecureContext: true},
|
||||
|
@ -59,7 +59,7 @@ var ecmaGlobals =
|
|||
{name: "Promise", insecureContext: true},
|
||||
{name: "Proxy", insecureContext: true},
|
||||
{name: "RangeError", insecureContext: true},
|
||||
{name: "ReadableStream", insecureContext: true, nightly: true},
|
||||
{name: "ReadableStream", insecureContext: true},
|
||||
{name: "ReferenceError", insecureContext: true},
|
||||
{name: "Reflect", insecureContext: true},
|
||||
{name: "RegExp", insecureContext: true},
|
||||
|
|
|
@ -1 +1 @@
|
|||
a2b4202242d937d328eda21c2d9fcfece609283e
|
||||
d771bae9f824769c73419fdc3ccffa2bdc47c3e4
|
||||
|
|
|
@ -10,7 +10,7 @@ use internal_types::{FastHashMap, FastHashSet};
|
|||
use print_tree::{PrintTree, PrintTreePrinter};
|
||||
use scene::SceneProperties;
|
||||
use smallvec::SmallVec;
|
||||
use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
|
||||
use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo, ScrollFrameKind};
|
||||
use util::{LayoutToWorldFastTransform, ScaleOffset};
|
||||
|
||||
pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
|
||||
|
@ -326,6 +326,7 @@ impl ClipScrollTree {
|
|||
frame_rect: &LayoutRect,
|
||||
content_size: &LayoutSize,
|
||||
scroll_sensitivity: ScrollSensitivity,
|
||||
frame_kind: ScrollFrameKind,
|
||||
) -> SpatialNodeIndex {
|
||||
let node = SpatialNode::new_scroll_frame(
|
||||
pipeline_id,
|
||||
|
@ -334,6 +335,7 @@ impl ClipScrollTree {
|
|||
frame_rect,
|
||||
content_size,
|
||||
scroll_sensitivity,
|
||||
frame_kind,
|
||||
);
|
||||
self.add_spatial_node(node)
|
||||
}
|
||||
|
@ -435,6 +437,53 @@ impl ClipScrollTree {
|
|||
self.print_node(self.root_reference_frame_index(), pt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if this is a guaranteed identity transform. This
|
||||
/// is conservative, it assumes not identity if a property
|
||||
/// binding animation, or scroll frame is found, for example.
|
||||
pub fn node_is_identity(
|
||||
&self,
|
||||
spatial_node_index: SpatialNodeIndex,
|
||||
) -> bool {
|
||||
let mut current = spatial_node_index;
|
||||
|
||||
while current != ROOT_SPATIAL_NODE_INDEX {
|
||||
let node = &self.spatial_nodes[current.0];
|
||||
|
||||
match node.node_type {
|
||||
SpatialNodeType::ReferenceFrame(ref info) => {
|
||||
if !info.source_perspective.is_identity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match info.source_transform {
|
||||
PropertyBinding::Value(transform) => {
|
||||
if transform != LayoutTransform::identity() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PropertyBinding::Binding(..) => {
|
||||
// Assume not identity since it may change with animation.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SpatialNodeType::ScrollFrame(ref info) => {
|
||||
// Assume not identity since it may change with scrolling.
|
||||
if let ScrollFrameKind::Explicit = info.frame_kind {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SpatialNodeType::StickyFrame(..) => {
|
||||
// Assume not identity since it may change with scrolling.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
current = node.parent.unwrap();
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -30,7 +30,7 @@ use render_backend::{DocumentView};
|
|||
use resource_cache::{FontInstanceMap, ImageRequest};
|
||||
use scene::{Scene, ScenePipeline, StackingContextHelpers};
|
||||
use scene_builder::DocumentResources;
|
||||
use spatial_node::{StickyFrameInfo};
|
||||
use spatial_node::{StickyFrameInfo, ScrollFrameKind};
|
||||
use std::{f32, mem};
|
||||
use std::collections::vec_deque::VecDeque;
|
||||
use tiling::{CompositeOps};
|
||||
|
@ -382,6 +382,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
&frame_rect,
|
||||
&content_rect.size,
|
||||
info.scroll_sensitivity,
|
||||
ScrollFrameKind::Explicit,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -499,6 +500,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
&iframe_rect,
|
||||
&pipeline.content_size,
|
||||
ScrollSensitivity::ScriptAndInputEvents,
|
||||
ScrollFrameKind::PipelineRoot,
|
||||
);
|
||||
|
||||
self.flatten_root(
|
||||
|
@ -1078,6 +1080,25 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
pub fn pop_stacking_context(&mut self) {
|
||||
let stacking_context = self.sc_stack.pop().unwrap();
|
||||
|
||||
// If we encounter a stacking context that is effectively a no-op, then instead
|
||||
// of creating a picture, just append the primitive list to the parent stacking
|
||||
// context as a short cut. This serves two purposes:
|
||||
// (a) It's an optimization to reduce picture count and allocations, as display lists
|
||||
// often contain a lot of these stacking contexts that don't require pictures or
|
||||
// off-screen surfaces.
|
||||
// (b) It's useful for the initial version of picture caching in gecko, by enabling
|
||||
// is to just look for interesting scroll roots on the root stacking context,
|
||||
// without having to consider cuts at stacking context boundaries.
|
||||
if let Some(parent_sc) = self.sc_stack.last_mut() {
|
||||
if stacking_context.is_redundant(
|
||||
parent_sc,
|
||||
self.clip_scroll_tree,
|
||||
) {
|
||||
parent_sc.primitives.extend(stacking_context.primitives);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// An arbitrary large clip rect. For now, we don't
|
||||
// specify a clip specific to the stacking context.
|
||||
// However, now that they are represented as Picture
|
||||
|
@ -1344,6 +1365,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
&LayoutRect::new(LayoutPoint::zero(), *viewport_size),
|
||||
content_size,
|
||||
ScrollSensitivity::ScriptAndInputEvents,
|
||||
ScrollFrameKind::PipelineRoot,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1455,6 +1477,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
frame_rect: &LayoutRect,
|
||||
content_size: &LayoutSize,
|
||||
scroll_sensitivity: ScrollSensitivity,
|
||||
frame_kind: ScrollFrameKind,
|
||||
) -> SpatialNodeIndex {
|
||||
let parent_node_index = self.id_to_index_mapper.get_spatial_node_index(parent_id);
|
||||
let node_index = self.clip_scroll_tree.add_scroll_frame(
|
||||
|
@ -1464,6 +1487,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
frame_rect,
|
||||
content_size,
|
||||
scroll_sensitivity,
|
||||
frame_kind,
|
||||
);
|
||||
self.id_to_index_mapper.map_spatial_node(new_node_id, node_index);
|
||||
self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
|
||||
|
@ -2185,6 +2209,51 @@ impl FlattenedStackingContext {
|
|||
self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
|
||||
}
|
||||
|
||||
/// Return true if the stacking context isn't needed.
|
||||
pub fn is_redundant(
|
||||
&self,
|
||||
parent: &FlattenedStackingContext,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
) -> bool {
|
||||
// Any 3d context is required
|
||||
if let Picture3DContext::In { .. } = self.context_3d {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are filters / mix-blend-mode
|
||||
if !self.composite_ops.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If backface visibility is different
|
||||
if self.primitive_data_handle.uid() != parent.primitive_data_handle.uid() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If rasterization space is different
|
||||
if self.requested_raster_space != parent.requested_raster_space {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If different clipp chains
|
||||
if self.clip_chain_id != parent.clip_chain_id {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If need to isolate in surface due to clipping / mix-blend-mode
|
||||
if self.should_isolate {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If represents a transform, it may affect backface visibility of children
|
||||
if !clip_scroll_tree.node_is_identity(self.spatial_node_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It is redundant!
|
||||
true
|
||||
}
|
||||
|
||||
/// For a Preserve3D context, cut the sequence of the immediate flat children
|
||||
/// recorded so far and generate a picture from them.
|
||||
pub fn cut_flat_item_sequence(
|
||||
|
|
|
@ -775,7 +775,7 @@ impl TileCache {
|
|||
// exact content of this tile.
|
||||
if let Some(handle) = retained_tiles.remove(&tile.descriptor) {
|
||||
// Only use if not evicted from texture cache in the meantime.
|
||||
if !resource_cache.texture_cache.request(&handle, gpu_cache) {
|
||||
if resource_cache.texture_cache.is_allocated(&handle) {
|
||||
// We found a matching tile from the previous scene, so use it!
|
||||
tile.handle = handle;
|
||||
tile.is_valid = true;
|
||||
|
@ -847,12 +847,19 @@ impl TileCache {
|
|||
.expect("bug: unable to map tile to world coords");
|
||||
tile.is_visible = frame_context.screen_world_rect.intersects(&tile_world_rect);
|
||||
|
||||
// If we have an invalid tile, which is also visible, add it to the
|
||||
// dirty rect we will need to draw.
|
||||
if !tile.is_valid && tile.is_visible && tile.in_use {
|
||||
dirty_rect = dirty_rect.union(&tile_rect);
|
||||
tile_offset.x = tile_offset.x.min(x);
|
||||
tile_offset.y = tile_offset.y.min(y);
|
||||
if tile.is_visible && tile.in_use {
|
||||
// Ensure we request the texture cache handle for this tile
|
||||
// each frame it will be used so the texture cache doesn't
|
||||
// decide to evict tiles that we currently want to use.
|
||||
resource_cache.texture_cache.request(&tile.handle, gpu_cache);
|
||||
|
||||
// If we have an invalid tile, which is also visible, add it to the
|
||||
// dirty rect we will need to draw.
|
||||
if !tile.is_valid {
|
||||
dirty_rect = dirty_rect.union(&tile_rect);
|
||||
tile_offset.x = tile_offset.x.min(x);
|
||||
tile_offset.y = tile_offset.y.min(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1063,6 +1063,10 @@ impl ResourceCache {
|
|||
|
||||
assert!(!descriptor.rect.is_empty());
|
||||
|
||||
if !self.blob_image_templates.contains_key(&request.key) {
|
||||
panic!("already missing blob image key {:?} deleted: {:?}", request, self.deleted_blob_keys);
|
||||
}
|
||||
|
||||
self.missing_blob_images.push(
|
||||
BlobImageParams {
|
||||
request,
|
||||
|
|
|
@ -96,6 +96,7 @@ impl SpatialNode {
|
|||
frame_rect: &LayoutRect,
|
||||
content_size: &LayoutSize,
|
||||
scroll_sensitivity: ScrollSensitivity,
|
||||
frame_kind: ScrollFrameKind,
|
||||
) -> Self {
|
||||
let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new(
|
||||
*frame_rect,
|
||||
|
@ -105,6 +106,7 @@ impl SpatialNode {
|
|||
(content_size.height - frame_rect.size.height).max(0.0)
|
||||
),
|
||||
external_id,
|
||||
frame_kind,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -558,6 +560,14 @@ impl SpatialNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Defines whether we have an implicit scroll frame for a pipeline root,
|
||||
/// or an explicitly defined scroll frame from the display list.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ScrollFrameKind {
|
||||
PipelineRoot,
|
||||
Explicit,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ScrollFrameInfo {
|
||||
/// The rectangle of the viewport of this scroll frame. This is important for
|
||||
|
@ -575,6 +585,14 @@ pub struct ScrollFrameInfo {
|
|||
/// which may change between frames.
|
||||
pub external_id: Option<ExternalScrollId>,
|
||||
|
||||
/// Stores whether this is a scroll frame added implicitly by WR when adding
|
||||
/// a pipeline (either the root or an iframe). We need to exclude these
|
||||
/// when searching for scroll roots we care about for picture caching.
|
||||
/// TODO(gw): I think we can actually completely remove the implicit
|
||||
/// scroll frame being added by WR, and rely on the embedder
|
||||
/// to define scroll frames. However, that involves API changes
|
||||
/// so we will use this as a temporary hack!
|
||||
pub frame_kind: ScrollFrameKind,
|
||||
}
|
||||
|
||||
/// Manages scrolling offset.
|
||||
|
@ -584,6 +602,7 @@ impl ScrollFrameInfo {
|
|||
scroll_sensitivity: ScrollSensitivity,
|
||||
scrollable_size: LayoutSize,
|
||||
external_id: Option<ExternalScrollId>,
|
||||
frame_kind: ScrollFrameKind,
|
||||
) -> ScrollFrameInfo {
|
||||
ScrollFrameInfo {
|
||||
viewport_rect,
|
||||
|
@ -591,6 +610,7 @@ impl ScrollFrameInfo {
|
|||
scroll_sensitivity,
|
||||
scrollable_size,
|
||||
external_id,
|
||||
frame_kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,6 +631,7 @@ impl ScrollFrameInfo {
|
|||
scroll_sensitivity: self.scroll_sensitivity,
|
||||
scrollable_size: self.scrollable_size,
|
||||
external_id: self.external_id,
|
||||
frame_kind: self.frame_kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -582,6 +582,18 @@ impl<Src, Dst> FastTransform<Src, Dst> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return true if this is an identity transform
|
||||
pub fn is_identity(&self)-> bool {
|
||||
match *self {
|
||||
FastTransform::Offset(offset) => {
|
||||
offset == TypedVector2D::zero()
|
||||
}
|
||||
FastTransform::Transform { ref transform, .. } => {
|
||||
*transform == TypedTransform3D::identity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pre_mul<NewSrc>(
|
||||
&self,
|
||||
|
|
|
@ -5,11 +5,11 @@ root:
|
|||
bounds: [10, 10, 100, 100]
|
||||
clip-rect: [10, 10, 100, 100]
|
||||
type: rect
|
||||
color: 0 256 0 1.0
|
||||
color: 0 255 0 1.0
|
||||
-
|
||||
bounds: [110, 10, 100, 100]
|
||||
clip-rect: [110, 10, 100, 100]
|
||||
type: rect
|
||||
color: 0 256 0 1.0
|
||||
color: 0 255 0 1.0
|
||||
id: [0, 1]
|
||||
pipelines: []
|
||||
|
|
|
@ -22,7 +22,7 @@ root:
|
|||
clip-rect: [0, 0, 100, 100]
|
||||
clip-and-scroll: root-reference-frame
|
||||
type: rect
|
||||
color: 0 256 0 1.0
|
||||
color: 0 255 0 1.0
|
||||
# The same test as above, except this time the stacking context also starts its
|
||||
# own reference frame.
|
||||
-
|
||||
|
@ -44,6 +44,6 @@ root:
|
|||
clip-rect: [0, 0, 100, 100]
|
||||
clip-and-scroll: 4
|
||||
type: rect
|
||||
color: 0 256 0 1.0
|
||||
color: 0 255 0 1.0
|
||||
id: [0, 1]
|
||||
pipelines: []
|
||||
|
|
|
@ -259,7 +259,7 @@ void Zone::discardJitCode(FreeOp* fop, bool discardBaselineCode,
|
|||
// because TypeScript contains the ICScript* and there's no need to
|
||||
// purge stubs if we just destroyed the Typescript.
|
||||
if (discardBaselineCode && script->hasICScript()) {
|
||||
script->icScript()->purgeOptimizedStubs(script->zone());
|
||||
script->icScript()->purgeOptimizedStubs(script);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
Object.defineProperty(this, "fuzzutils", {
|
||||
value: {
|
||||
orig_evaluate: evaluate,
|
||||
evaluate: function(c, o) {
|
||||
if (!o) {
|
||||
o = {};
|
||||
}
|
||||
o.catchTermination = true;
|
||||
return fuzzutils.orig_evaluate(c, o);
|
||||
},
|
||||
}
|
||||
});
|
||||
gczeal(21, 10);
|
||||
fuzzutils.evaluate(`
|
||||
enableShellAllocationMetadataBuilder();
|
||||
function test() {}
|
||||
`);
|
|
@ -345,7 +345,7 @@ class ICScript {
|
|||
void noteHasDenseAdd(uint32_t pcOffset);
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
void purgeOptimizedStubs(Zone* zone);
|
||||
void purgeOptimizedStubs(JSScript* script);
|
||||
|
||||
ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
|
||||
ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
|
||||
|
|
|
@ -1014,7 +1014,19 @@ void BaselineScript::toggleProfilerInstrumentation(bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
void ICScript::purgeOptimizedStubs(Zone* zone) {
|
||||
void ICScript::purgeOptimizedStubs(JSScript* script) {
|
||||
MOZ_ASSERT(script->icScript() == this);
|
||||
|
||||
Zone* zone = script->zone();
|
||||
if (zone->isGCSweeping() && IsAboutToBeFinalizedDuringSweep(*script)) {
|
||||
// We're sweeping and the script is dead. Don't purge optimized stubs
|
||||
// because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
|
||||
// because we may have swept them already when we started (incremental)
|
||||
// sweeping and (2) it's unnecessary because this script will be finalized
|
||||
// soon anyway.
|
||||
return;
|
||||
}
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
|
||||
|
||||
for (size_t i = 0; i < numICEntries(); i++) {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
#container {
|
||||
/*
|
||||
* This value should be greater than reftest viewport width 800px * (1 / 0.25).
|
||||
* 0.25 is the initial scale value used in the reference.
|
||||
*/
|
||||
min-width: 4000px;
|
||||
/*
|
||||
* This value should be greater than reftest viewport height
|
||||
* 1000x * (1 / 0.25) to make the initial scale auto-calculation happen.
|
||||
*/
|
||||
min-height: 8000px;
|
||||
position: relative;
|
||||
}
|
||||
#inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
<div id="container">
|
||||
<div id="inner"></div>
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<meta name="viewport" content="initial-scale=0.25,width=device-width">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#container {
|
||||
position: relative;
|
||||
}
|
||||
#inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
<div id="container">
|
||||
<div id="inner"></div>
|
||||
</div>
|
|
@ -8,3 +8,4 @@ default-preferences pref(dom.meta-viewport.enabled,true) pref(apz.allow_zooming,
|
|||
== viewport-width.html initial-scale-0_5-ref.html
|
||||
== initial-scale-1.html no-zoom-ref.html
|
||||
== minimum-scale.html no-zoom-ref.html
|
||||
== clamped-by-default-minimum-scale.html initial-scale-0_25-ref.html
|
||||
|
|
|
@ -148,19 +148,6 @@ bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
|||
continue;
|
||||
}
|
||||
|
||||
//Skip windows of zero area
|
||||
CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowBounds));
|
||||
CGRect bounds_rect;
|
||||
if(!(bounds_ref) ||
|
||||
!(CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds_rect))){
|
||||
continue;
|
||||
}
|
||||
bounds_rect = CGRectStandardize(bounds_rect);
|
||||
if((bounds_rect.size.width <= 0) || (bounds_rect.size.height <= 0)){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip windows with layer=0 (menu, dock).
|
||||
// TODO(zijiehe): The windows with layer != 0 are skipped, is this a bug in
|
||||
// code (not likely) or a bug in comments? What's the meaning of window
|
||||
|
|
|
@ -82,13 +82,6 @@ BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) {
|
|||
if (window.title.empty())
|
||||
return TRUE;
|
||||
|
||||
// Skip windows of zero visible area, except IconicWindows
|
||||
RECT bounds;
|
||||
if(GetClientRect(hwnd,&bounds) && !IsIconic(hwnd)
|
||||
&& IsRectEmpty(&bounds)){
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
list->push_back(window);
|
||||
|
||||
return TRUE;
|
||||
|
|
|
@ -209,15 +209,6 @@ bool GetWindowList(XAtomCache* cache,
|
|||
::Window app_window =
|
||||
GetApplicationWindow(cache, children[num_children - 1 - i]);
|
||||
if (app_window && !IsDesktopElement(cache, app_window)) {
|
||||
XWindowAttributes window_attr;
|
||||
if(!XGetWindowAttributes(display, app_window, &window_attr)) {
|
||||
RTC_LOG(LS_ERROR)<<"Bad request for attributes for window ID:" << app_window;
|
||||
continue;
|
||||
}
|
||||
if((window_attr.width <= 0) || (window_attr.height <=0)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!on_window(app_window)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -505,7 +505,8 @@ class AccessibilityTest : BaseSessionTest() {
|
|||
@Test fun testScroll() {
|
||||
var nodeId = View.NO_ID
|
||||
sessionRule.session.loadString(
|
||||
"""<body style="margin: 0;">
|
||||
"""<meta name="viewport" content="width=device-width initial-scale=1">
|
||||
<body style="margin: 0;">
|
||||
<div style="height: 100vh;"></div>
|
||||
<button>Hello</button>
|
||||
<p style="margin: 0;">Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
|
|
|
@ -1575,11 +1575,7 @@ pref("javascript.options.spectre.jit_to_C++_calls", true);
|
|||
#endif
|
||||
|
||||
// Streams API
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("javascript.options.streams", true);
|
||||
#else
|
||||
pref("javascript.options.streams", false);
|
||||
#endif
|
||||
|
||||
// BigInt API
|
||||
pref("javascript.options.bigint", false);
|
||||
|
@ -5142,7 +5138,7 @@ pref("extensions.webextensions.performanceCountersMaxAge", 1000);
|
|||
|
||||
// Report Site Issue button
|
||||
pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new");
|
||||
#if defined(MOZ_DEV_EDITION) || defined(NIGHTLY_BUILD)
|
||||
#if MOZ_UPDATE_CHANNEL != release && MOZ_UPDATE_CHANNEL != esr
|
||||
pref("extensions.webcompat-reporter.enabled", true);
|
||||
#else
|
||||
pref("extensions.webcompat-reporter.enabled", false);
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
from __future__ import absolute_import
|
||||
import fluent.syntax.ast as FTL
|
||||
from fluent.migrate.helpers import transforms_from
|
||||
from fluent.migrate import CONCAT
|
||||
from fluent.migrate import REPLACE
|
||||
from fluent.migrate import COPY
|
||||
|
||||
|
||||
def migrate(ctx):
|
||||
""" Bug 1504751 - Migrate about:Networking to Fluent, part {index}. """
|
||||
|
||||
ctx.add_transforms(
|
||||
"toolkit/toolkit/about/aboutNetworking.ftl",
|
||||
"toolkit/toolkit/about/aboutNetworking.ftl",
|
||||
transforms_from(
|
||||
"""
|
||||
title = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.title")}
|
||||
warning = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.warning")}
|
||||
show-next-time-checkbox = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.showNextTime")}
|
||||
ok = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.ok")}
|
||||
http = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.HTTP")}
|
||||
sockets = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.sockets")}
|
||||
dns = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.dns")}
|
||||
websockets = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.websockets")}
|
||||
refresh = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.refresh")}
|
||||
auto-refresh = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.autoRefresh")}
|
||||
hostname = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.hostname")}
|
||||
port = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.port")}
|
||||
http2 = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.http2")}
|
||||
ssl = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.ssl")}
|
||||
active = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.active")}
|
||||
idle = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.idle")}
|
||||
host = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.host")}
|
||||
tcp = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.tcp")}
|
||||
sent = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.sent")}
|
||||
received = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.received")}
|
||||
family = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.family")}
|
||||
trr = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.trr")}
|
||||
addresses = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.addresses")}
|
||||
expires = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.expires")}
|
||||
messages-sent = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.messagesSent")}
|
||||
messages-received = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.messagesReceived")}
|
||||
bytes-sent = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.bytesSent")}
|
||||
bytes-received = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.bytesReceived")}
|
||||
logging = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.logging")}
|
||||
current-log-file = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.currentLogFile")}
|
||||
current-log-modules = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.currentLogModules")}
|
||||
set-log-file = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.setLogFile")}
|
||||
set-log-modules = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.setLogModules")}
|
||||
start-logging = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.startLogging")}
|
||||
stop-logging = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.stopLogging")}
|
||||
dns-lookup = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.dnsLookup")}
|
||||
dns-lookup-button = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.dnsLookupButton")}
|
||||
dns-lookup-table-column = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.dnsLookupTableColumn")}
|
||||
rcwn = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwn")}
|
||||
rcwn-status = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnStatus")}
|
||||
rcwn-cache-won-count = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnCacheWonCount")}
|
||||
rcwn-net-won-count = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnNetWonCount")}
|
||||
total-network-requests = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.totalNetworkRequests")}
|
||||
rcwn-operation = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnOperation")}
|
||||
rcwn-perf-open = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnPerfOpen")}
|
||||
rcwn-perf-read = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnPerfRead")}
|
||||
rcwn-perf-write = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnPerfWrite")}
|
||||
rcwn-perf-entry-open = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnPerfEntryOpen")}
|
||||
rcwn-avg-short = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnAvgShort")}
|
||||
rcwn-avg-long = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnAvgLong")}
|
||||
rcwn-std-dev-long = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnStddevLong")}
|
||||
rcwn-cache-slow = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnCacheSlow")}
|
||||
rcwn-cache-not-slow = { COPY("toolkit/chrome/global/aboutNetworking.dtd", "aboutNetworking.rcwnCacheNotSlow")}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
ctx.add_transforms(
|
||||
"toolkit/toolkit/about/aboutNetworking.ftl",
|
||||
"toolkit/toolkit/about/aboutNetworking.ftl",
|
||||
[
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("log-tutorial"),
|
||||
value=REPLACE(
|
||||
"toolkit/chrome/global/aboutNetworking.dtd",
|
||||
"aboutNetworking.logTutorial",
|
||||
{
|
||||
"href='https://developer.mozilla.org/docs/Mozilla/Debugging/HTTP_logging'": FTL.TextElement('data-l10n-name="logging"')
|
||||
}
|
||||
)
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("dns-domain"),
|
||||
value=CONCAT(
|
||||
COPY(
|
||||
"toolkit/chrome/global/aboutNetworking.dtd",
|
||||
"aboutNetworking.dnsDomain",
|
||||
),
|
||||
FTL.TextElement(":"),
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
|
@ -0,0 +1,111 @@
|
|||
# coding=utf8
|
||||
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
from __future__ import absolute_import
|
||||
import fluent.syntax.ast as FTL
|
||||
from fluent.migrate.helpers import transforms_from
|
||||
from fluent.migrate.helpers import TERM_REFERENCE
|
||||
from fluent.migrate import COPY
|
||||
from fluent.migrate import REPLACE
|
||||
from fluent.migrate import CONCAT
|
||||
|
||||
def migrate(ctx):
|
||||
"""Bug 1505846 Migrate about:searchreset to Fluent, part {index} """
|
||||
|
||||
ctx.add_transforms(
|
||||
|
||||
"browser/browser/aboutSearchReset.ftl",
|
||||
"browser/browser/aboutSearchReset.ftl",
|
||||
transforms_from(
|
||||
"""
|
||||
tab-title = { COPY("browser/chrome/browser/aboutSearchReset.dtd", "searchreset.tabtitle") }
|
||||
page-title = { COPY("browser/chrome/browser/aboutSearchReset.dtd", "searchreset.pageTitle") }
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
ctx.add_transforms(
|
||||
"browser/browser/aboutSearchReset.ftl",
|
||||
"browser/browser/aboutSearchReset.ftl",
|
||||
[
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("page-info-outofdate"),
|
||||
value=REPLACE(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.pageInfo1",
|
||||
{
|
||||
"&brandShortName;": TERM_REFERENCE("-brand-short-name"),
|
||||
}
|
||||
)
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("page-info-how-to-change"),
|
||||
value=CONCAT(
|
||||
COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.beforelink.pageInfo2",
|
||||
),
|
||||
FTL.TextElement('<a data-l10n-name="link">'),
|
||||
COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.link.pageInfo2",
|
||||
),
|
||||
FTL.TextElement("</a>"),
|
||||
COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.afterlink.pageInfo2",
|
||||
)
|
||||
)
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("no-change-button"),
|
||||
attributes=[
|
||||
FTL.Attribute(
|
||||
id=FTL.Identifier("label"),
|
||||
value=COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.noChangeButton",
|
||||
)
|
||||
),
|
||||
FTL.Attribute(
|
||||
id=FTL.Identifier("accesskey"),
|
||||
value=COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.noChangeButton.access",
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("change-engine-button"),
|
||||
attributes=[
|
||||
FTL.Attribute(
|
||||
id=FTL.Identifier("label"),
|
||||
value=COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.changeEngineButton",
|
||||
)
|
||||
),
|
||||
FTL.Attribute(
|
||||
id=FTL.Identifier("accesskey"),
|
||||
value=COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.changeEngineButton.access",
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("page-info-new-search-engine"),
|
||||
value=CONCAT(
|
||||
COPY(
|
||||
"browser/chrome/browser/aboutSearchReset.dtd",
|
||||
"searchreset.selector.label",
|
||||
),
|
||||
FTL.TextElement(' <span data-l10n-name="default-engine">{ $searchEngine }</span>'),
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
|
@ -53,8 +53,8 @@ def check_executable_version(exe, wrap_call_with_node=False):
|
|||
May raise ``subprocess.CalledProcessError`` or ``ValueError`` on failure.
|
||||
"""
|
||||
out = None
|
||||
# npm may be a script, so we must call it with node.
|
||||
if wrap_call_with_node:
|
||||
# npm may be a script (Except on Windows), so we must call it with node.
|
||||
if wrap_call_with_node and platform.system() != "Windows":
|
||||
binary, _ = find_node_executable()
|
||||
if binary:
|
||||
out = subprocess.check_output([binary, exe, "--version"]).lstrip('v').rstrip()
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include <glib.h>
|
||||
#ifdef MOZ_WAYLAND
|
||||
#include <gdk/gdk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
@ -386,12 +390,20 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory() {
|
|||
}
|
||||
policy->AddPath(SandboxBroker::MAY_CONNECT, bumblebeeSocket);
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
// Allow local X11 connections, for Primus and VirtualGL to contact
|
||||
// the secondary X server.
|
||||
// the secondary X server. No exception for Wayland.
|
||||
#if defined(MOZ_WAYLAND)
|
||||
if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
|
||||
policy->AddPrefix(SandboxBroker::MAY_CONNECT, "/tmp/.X11-unix/X");
|
||||
}
|
||||
#else
|
||||
policy->AddPrefix(SandboxBroker::MAY_CONNECT, "/tmp/.X11-unix/X");
|
||||
#endif
|
||||
if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
|
||||
policy->AddPath(rdonly, xauth);
|
||||
}
|
||||
#endif
|
||||
|
||||
mCommonContentPolicy.reset(policy);
|
||||
#endif
|
||||
|
|
|
@ -32,5 +32,6 @@ LOCAL_INCLUDES += [
|
|||
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
CXXFLAGS += CONFIG['GLIB_CFLAGS']
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -31,6 +31,11 @@ The ``get()`` method returns the list of entries for a specific key. Each entry
|
|||
// await InternalAPI.load(entry.id, entry.label, entry.weight);
|
||||
});
|
||||
|
||||
.. note::
|
||||
The data updates are managed internally, and ``.get()`` only returns the local data.
|
||||
The data is pulled from the server only if this collection has no local data yet and no JSON dump
|
||||
could be found (see :ref:`services/initial-data` below).
|
||||
|
||||
.. note::
|
||||
The ``id`` and ``last_modified`` (timestamp) attributes are assigned by the server.
|
||||
|
||||
|
@ -89,18 +94,23 @@ When an entry has a file attached to it, it has an ``attachment`` attribute, whi
|
|||
}
|
||||
});
|
||||
|
||||
.. _services/initial-data:
|
||||
|
||||
Initial data
|
||||
------------
|
||||
|
||||
For newly created user profiles, the list of entries returned by the ``.get()`` method will be empty until the first synchronization happens.
|
||||
It is possible to package a dump of the server records that will be loaded into the local database when no synchronization has happened yet.
|
||||
|
||||
It is possible to package a dump of the server records that will be loaded into the local database when no synchronization has happened yet. It will thus serve as the default dataset and also reduce the amount of data to be downloaded on the first synchronization.
|
||||
The JSON dump will serve as the default dataset for ``.get()``, instead of doing a round-trip to pull the latest data. It will also reduce the amount of data to be downloaded on the first synchronization.
|
||||
|
||||
#. Place the JSON dump of the server records in the ``services/settings/dumps/main/`` folder
|
||||
#. Add the filename to the ``FINAL_TARGET_FILES`` list in ``services/settings/dumps/main/moz.build``
|
||||
|
||||
Now, when ``RemoteSettings("some-key").get()`` is called from an empty profile, the ``some-key.json`` file is going to be loaded before the results are returned.
|
||||
|
||||
.. note::
|
||||
|
||||
JSON dumps are not shipped on Android to minimize the installer size.
|
||||
|
||||
Targets and A/B testing
|
||||
=======================
|
||||
|
@ -170,14 +180,15 @@ The synchronization of every known remote settings clients can be triggered manu
|
|||
|
||||
await RemoteSettings.pollChanges()
|
||||
|
||||
The synchronization of a single client can be forced with ``maybeSync()``:
|
||||
The synchronization of a single client can be forced with the ``.sync()`` method:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
const fakeTimestamp = Infinity;
|
||||
const fakeServerTime = Date.now();
|
||||
await RemoteSettings("a-key").sync();
|
||||
|
||||
await RemoteSettings("a-key").maybeSync(fakeTimestamp, fakeServerTime)
|
||||
.. important::
|
||||
|
||||
The above methods are only relevant during development or debugging and should never be called in production code.
|
||||
|
||||
|
||||
Manipulate local data
|
||||
|
|
|
@ -51,7 +51,7 @@ add_task(async function test_something() {
|
|||
server.registerPathHandler(recordsPath, handleResponse);
|
||||
|
||||
// Test an empty db populates
|
||||
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
|
||||
await OneCRLBlocklistClient.maybeSync(2000);
|
||||
|
||||
// Open the collection, verify it's been populated:
|
||||
const list = await OneCRLBlocklistClient.get();
|
||||
|
@ -63,9 +63,7 @@ add_task(async function test_something() {
|
|||
Services.prefs.clearUserPref("services.settings.server");
|
||||
Services.prefs.setIntPref("services.blocklist.onecrl.checked", 0);
|
||||
// Use any last_modified older than highest shipped in JSON dump.
|
||||
await OneCRLBlocklistClient.maybeSync(123456, Date.now());
|
||||
// Last check value was updated.
|
||||
Assert.notEqual(0, Services.prefs.getIntPref("services.blocklist.onecrl.checked"));
|
||||
await OneCRLBlocklistClient.maybeSync(123456);
|
||||
|
||||
// Restore server pref.
|
||||
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
|
||||
|
@ -78,7 +76,7 @@ add_task(async function test_something() {
|
|||
// single record
|
||||
await collection.db.saveLastModified(1000);
|
||||
|
||||
await OneCRLBlocklistClient.maybeSync(2000, Date.now());
|
||||
await OneCRLBlocklistClient.maybeSync(2000);
|
||||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
|
@ -86,7 +84,7 @@ add_task(async function test_something() {
|
|||
Assert.equal(before.length, 1);
|
||||
|
||||
// Test the db is updated when we call again with a later lastModified value
|
||||
await OneCRLBlocklistClient.maybeSync(4000, Date.now());
|
||||
await OneCRLBlocklistClient.maybeSync(4000);
|
||||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our test data now has two records; both should be in the local collection
|
||||
|
@ -97,23 +95,16 @@ add_task(async function test_something() {
|
|||
// should be attempted.
|
||||
// Clear the kinto base pref so any connections will cause a test failure
|
||||
Services.prefs.clearUserPref("services.settings.server");
|
||||
await OneCRLBlocklistClient.maybeSync(4000, Date.now());
|
||||
await OneCRLBlocklistClient.maybeSync(4000);
|
||||
|
||||
// Try again with a lastModified value at some point in the past
|
||||
await OneCRLBlocklistClient.maybeSync(3000, Date.now());
|
||||
|
||||
// Check the OneCRL check time pref is modified, even if the collection
|
||||
// hasn't changed
|
||||
Services.prefs.setIntPref("services.blocklist.onecrl.checked", 0);
|
||||
await OneCRLBlocklistClient.maybeSync(3000, Date.now());
|
||||
let newValue = Services.prefs.getIntPref("services.blocklist.onecrl.checked");
|
||||
Assert.notEqual(newValue, 0);
|
||||
await OneCRLBlocklistClient.maybeSync(3000);
|
||||
|
||||
// Check that a sync completes even when there's bad data in the
|
||||
// collection. This will throw on fail, so just calling maybeSync is an
|
||||
// acceptible test.
|
||||
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
|
||||
await OneCRLBlocklistClient.maybeSync(5000, Date.now());
|
||||
await OneCRLBlocklistClient.maybeSync(5000);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
|
|
|
@ -102,7 +102,7 @@ add_task(async function test_initial_dump_is_loaded_as_synced_when_collection_is
|
|||
}
|
||||
|
||||
// Test an empty db populates, but don't reach server (specified timestamp <= dump).
|
||||
await client.maybeSync(1, Date.now());
|
||||
await client.maybeSync(1);
|
||||
|
||||
// Verify the loaded data has status to synced:
|
||||
const collection = await client.openCollection();
|
||||
|
@ -134,19 +134,6 @@ add_task(async function test_initial_dump_is_loaded_when_using_get_on_empty_coll
|
|||
});
|
||||
add_task(clear_state);
|
||||
|
||||
add_task(async function test_current_server_time_is_saved_in_pref() {
|
||||
for (let {client} of gBlocklistClients) {
|
||||
// The lastCheckTimePref was customized:
|
||||
ok(/services\.blocklist\.(\w+)\.checked/.test(client.lastCheckTimePref), client.lastCheckTimePref);
|
||||
|
||||
const serverTime = Date.now();
|
||||
await client.maybeSync(3000, serverTime);
|
||||
const after = Services.prefs.getIntPref(client.lastCheckTimePref);
|
||||
equal(after, Math.round(serverTime / 1000));
|
||||
}
|
||||
});
|
||||
add_task(clear_state);
|
||||
|
||||
add_task(async function test_sync_event_data_is_filtered_for_target() {
|
||||
// Here we will synchronize 4 times, the first two to initialize the local DB and
|
||||
// the last two about event filtered data.
|
||||
|
@ -154,25 +141,23 @@ add_task(async function test_sync_event_data_is_filtered_for_target() {
|
|||
const timestamp2 = 3001;
|
||||
const timestamp3 = 4001;
|
||||
const timestamp4 = 5001;
|
||||
// Fake a date value obtained from server (used to store a pref, useless here).
|
||||
const fakeServerTime = Date.now();
|
||||
|
||||
for (let {client} of gBlocklistClients) {
|
||||
// Initialize the collection with some data (local is empty, thus no ?_since)
|
||||
await client.maybeSync(timestamp1, fakeServerTime - 30, {loadDump: false});
|
||||
await client.maybeSync(timestamp1, {loadDump: false});
|
||||
// This will pick the data with ?_since=3000.
|
||||
await client.maybeSync(timestamp2, fakeServerTime - 20);
|
||||
await client.maybeSync(timestamp2);
|
||||
|
||||
// In ?_since=4000 entries, no target matches. The sync event is not called.
|
||||
let called = false;
|
||||
client.on("sync", e => called = true);
|
||||
await client.maybeSync(timestamp3, fakeServerTime - 10);
|
||||
await client.maybeSync(timestamp3);
|
||||
equal(called, false, `shouldn't have sync event for ${client.collectionName}`);
|
||||
|
||||
// In ?_since=5000 entries, only one entry matches.
|
||||
let syncEventData;
|
||||
client.on("sync", e => syncEventData = e.data);
|
||||
await client.maybeSync(timestamp4, fakeServerTime);
|
||||
await client.maybeSync(timestamp4);
|
||||
const { current, created, updated, deleted } = syncEventData;
|
||||
equal(created.length + updated.length + deleted.length, 1, `event filtered data for ${client.collectionName}`);
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ add_task(async function test_something() {
|
|||
Services.io.newURI("https://five.example.com"), 0));
|
||||
|
||||
// Test an empty db populates
|
||||
await PinningPreloadClient.maybeSync(2000, Date.now());
|
||||
await PinningPreloadClient.maybeSync(2000);
|
||||
|
||||
// Open the collection, verify it's been populated:
|
||||
// Our test data has a single record; it should be in the local collection
|
||||
|
@ -92,7 +92,7 @@ add_task(async function test_something() {
|
|||
Services.io.newURI("https://one.example.com"), 0));
|
||||
|
||||
// Test the db is updated when we call again with a later lastModified value
|
||||
await PinningPreloadClient.maybeSync(4000, Date.now());
|
||||
await PinningPreloadClient.maybeSync(4000);
|
||||
|
||||
// Open the collection, verify it's been updated:
|
||||
// Our data now has four new records; all should be in the local collection
|
||||
|
@ -114,17 +114,10 @@ add_task(async function test_something() {
|
|||
// should be attempted.
|
||||
// Clear the kinto base pref so any connections will cause a test failure
|
||||
Services.prefs.clearUserPref("services.settings.server");
|
||||
await PinningPreloadClient.maybeSync(4000, Date.now());
|
||||
await PinningPreloadClient.maybeSync(4000);
|
||||
|
||||
// Try again with a lastModified value at some point in the past
|
||||
await PinningPreloadClient.maybeSync(3000, Date.now());
|
||||
|
||||
// Check the pinning check time pref is modified, even if the collection
|
||||
// hasn't changed
|
||||
Services.prefs.setIntPref("services.blocklist.onecrl.checked", 0);
|
||||
await PinningPreloadClient.maybeSync(3000, Date.now());
|
||||
let newValue = Services.prefs.getIntPref("services.blocklist.pinning.checked");
|
||||
Assert.notEqual(newValue, 0);
|
||||
await PinningPreloadClient.maybeSync(3000);
|
||||
|
||||
// Check that the HSTS preload added to the collection works...
|
||||
ok(sss.isSecureURI(sss.HEADER_HSTS,
|
||||
|
@ -139,7 +132,7 @@ add_task(async function test_something() {
|
|||
// acceptible test (the data below with last_modified of 300 is nonsense).
|
||||
Services.prefs.setCharPref("services.settings.server",
|
||||
`http://localhost:${server.identity.primaryPort}/v1`);
|
||||
await PinningPreloadClient.maybeSync(5000, Date.now());
|
||||
await PinningPreloadClient.maybeSync(5000);
|
||||
|
||||
// The STS entry for five.example.com now has includeSubdomains set;
|
||||
// ensure that the new includeSubdomains value is honored.
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче