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

This commit is contained in:
Ciure Andrei 2018-10-31 00:16:17 +02:00
Родитель 93804cc834 499182b91a
Коммит a2c4dad0d5
349 изменённых файлов: 34028 добавлений и 4674 удалений

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

@ -37,6 +37,7 @@ browser/extensions/mortar/ppapi/.*
db/sqlite3/src/.*
devtools/client/sourceeditor/codemirror/.*
devtools/client/sourceeditor/tern/.*
dom/canvas/test/webgl-conf/checkout/closure-library/
dom/media/gmp/widevine-adapter/content_decryption_module.h
dom/media/gmp/widevine-adapter/content_decryption_module_export.h
dom/media/gmp/widevine-adapter/content_decryption_module_ext.h

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

@ -67,7 +67,7 @@ skip-if = verify
[browser_favicon_firstParty.js]
[browser_favicon_userContextId.js]
[browser_firstPartyIsolation.js]
skip-if = verify || debug #Bug 1345346
skip-if = debug #Bug 1345346
[browser_firstPartyIsolation_about_newtab.js]
[browser_firstPartyIsolation_aboutPages.js]
[browser_firstPartyIsolation_blobURI.js]

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

@ -1,4 +1,5 @@
const BASE_URL = "http://mochi.test:8888/browser/browser/components/originattributes/test/browser/";
const EXAMPLE_BASE_URL = BASE_URL.replace("mochi.test:8888", "example.com");
const BASE_DOMAIN = "mochi.test";
add_task(async function setup() {
@ -6,6 +7,7 @@ add_task(async function setup() {
registerCleanupFunction(function() {
Services.prefs.clearUserPref("privacy.firstparty.isolate");
Services.cookies.removeAll();
Services.cache2.clear();
});
});
@ -182,8 +184,7 @@ add_task(async function window_open_redirect_test() {
});
let tab = BrowserTestUtils.addTab(gBrowser, BASE_URL + "window_redirect.html");
let win = await BrowserTestUtils.waitForNewWindow();
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
let win = await BrowserTestUtils.waitForNewWindow({url: BASE_URL + "dummy.html"});
await ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, async function(attrs) {
Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
@ -209,8 +210,9 @@ add_task(async function window_open_iframe_test() {
});
let tab = BrowserTestUtils.addTab(gBrowser, BASE_URL + "window2.html");
let win = await BrowserTestUtils.waitForNewWindow();
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser, true);
let url = EXAMPLE_BASE_URL + "test_firstParty.html";
info("Waiting for window with url " + url);
let win = await BrowserTestUtils.waitForNewWindow({url});
await ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, async function(attrs) {
Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
@ -260,8 +262,9 @@ add_task(async function window_open_form_test() {
});
let tab = BrowserTestUtils.addTab(gBrowser, BASE_URL + "window3.html");
let win = await BrowserTestUtils.waitForNewWindow();
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser, true);
let url = EXAMPLE_BASE_URL + "test_form.html";
info("Waiting for window with url " + url);
let win = await BrowserTestUtils.waitForNewWindow({url});
await ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, async function(attrs) {
Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,

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

@ -1927,7 +1927,7 @@ FormAutofillStorage.prototype = {
this._initializePromise = this._store.load()
.then(() => {
let initializeAutofillRecords = [this.addresses.initialize()];
if (FormAutofill.isAutofillCreditCardsEnabled) {
if (FormAutofill.isAutofillCreditCardsAvailable) {
initializeAutofillRecords.push(this.creditCards.initialize());
} else {
// Make creditCards records unavailable to other modules

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

@ -672,3 +672,35 @@ add_task(async function test_getDuplicateGuid() {
record["cc-number"] = "************" + last4Digits;
Assert.equal(await profileStorage.creditCards.getDuplicateGuid(record), null);
});
add_task(async function test_creditCardFillDisabled() {
Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
Assert.equal(!!profileStorage.creditCards, true,
"credit card records initialized and available.");
Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", true);
});
add_task(async function test_creditCardFillUnavailable() {
Services.prefs.setBoolPref("extensions.formautofill.creditCards.available", false);
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
try {
profileStorage.creditCards; // eslint-disable-line no-unused-expressions
throw new Error("Access credit card didn't throw.");
} catch (err) {
Assert.equal(err.message,
"CreditCards is not initialized. " +
"Please restart if you flip the pref manually.");
}
Services.prefs.setBoolPref("extensions.formautofill.creditCards.available", true);
});

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

@ -18,8 +18,10 @@ mozilla.pth:third_party/python/enum34
mozilla.pth:third_party/python/fluent
mozilla.pth:third_party/python/funcsigs
mozilla.pth:third_party/python/futures
mozilla.pth:third_party/python/mohawk
mozilla.pth:third_party/python/more-itertools
mozilla.pth:third_party/python/mozilla-version
mozilla.pth:third_party/python/pathlib2
mozilla.pth:third_party/python/gyp/pylib
mozilla.pth:third_party/python/python-hglib
mozilla.pth:third_party/python/pluggy
@ -33,7 +35,10 @@ mozilla.pth:third_party/python/pystache
mozilla.pth:third_party/python/pyyaml/lib
mozilla.pth:third_party/python/requests
mozilla.pth:third_party/python/requests-unixsocket
mozilla.pth:third_party/python/scandir
mozilla.pth:third_party/python/slugid
mozilla.pth:third_party/python/taskcluster
mozilla.pth:third_party/python/taskcluster-urls
mozilla.pth:third_party/python/py
mozilla.pth:third_party/python/pytest/src
mozilla.pth:third_party/python/pytoml

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

@ -60,7 +60,7 @@ function debugTargetListenerMiddleware(store) {
const { client } = runtime.runtimeDetails;
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
client.addListener("tabListChanged", onTabsUpdated);
client.mainRoot.on("tabListChanged", onTabsUpdated);
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
@ -68,9 +68,9 @@ function debugTargetListenerMiddleware(store) {
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
client.addListener("workerListChanged", onWorkersUpdated);
client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.addListener("processListChanged", onWorkersUpdated);
client.mainRoot.on("workerListChanged", onWorkersUpdated);
client.mainRoot.on("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.mainRoot.on("processListChanged", onWorkersUpdated);
client.addListener("registration-changed", onWorkersUpdated);
client.addListener("push-subscription-modified", onWorkersUpdated);
}
@ -81,7 +81,7 @@ function debugTargetListenerMiddleware(store) {
const { client } = runtime.runtimeDetails;
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
client.removeListener("tabListChanged", onTabsUpdated);
client.mainRoot.off("tabListChanged", onTabsUpdated);
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
@ -89,9 +89,9 @@ function debugTargetListenerMiddleware(store) {
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
client.removeListener("workerListChanged", onWorkersUpdated);
client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.removeListener("processListChanged", onWorkersUpdated);
client.mainRoot.off("workerListChanged", onWorkersUpdated);
client.mainRoot.off("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.mainRoot.off("processListChanged", onWorkersUpdated);
client.removeListener("registration-changed", onWorkersUpdated);
client.removeListener("push-subscription-modified", onWorkersUpdated);
}

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

@ -8,6 +8,7 @@ support-files =
resources/test-adb-extension/*
resources/test-temporary-extension/*
!/devtools/client/shared/test/shared-head.js
!/devtools/client/shared/test/shared-redux-head.js
!/devtools/client/shared/test/telemetry-test-helpers.js
[browser_aboutdebugging_connect_networklocations.js]

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

@ -119,6 +119,9 @@ class AddonsPanel extends Component {
});
this.setState({ extensions });
const { AboutDebugging } = window;
AboutDebugging.emit("addons-updated");
}, error => {
throw new Error("Client error while listing addons: " + error);
});

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

@ -42,13 +42,13 @@ class TabsPanel extends Component {
componentDidMount() {
const { client } = this.props;
client.addListener("tabListChanged", this.update);
client.mainRoot.on("tabListChanged", this.update);
this.update();
}
componentWillUnmount() {
const { client } = this.props;
client.removeListener("tabListChanged", this.update);
client.mainRoot.off("tabListChanged", this.update);
}
async update() {

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

@ -54,9 +54,16 @@ class WorkersPanel extends Component {
componentDidMount() {
const client = this.props.client;
// When calling RootFront.listAllWorkers, ContentProcessTargetActor are created
// for each content process, which sends `workerListChanged` events.
// Until we create a Front for ContentProcessTargetActor, we should listen for these
// event on DebuggerClient. After that, we have to listen on the related fronts
// directly.
client.addListener("workerListChanged", this.updateWorkers);
client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
client.addListener("processListChanged", this.updateWorkers);
client.mainRoot.on("workerListChanged", this.updateWorkers);
client.mainRoot.on("serviceWorkerRegistrationListChanged", this.updateWorkers);
client.mainRoot.on("processListChanged", this.updateWorkers);
client.addListener("registration-changed", this.updateWorkers);
// Some notes about these observers:
@ -80,8 +87,9 @@ class WorkersPanel extends Component {
componentWillUnmount() {
const client = this.props.client;
client.removeListener("processListChanged", this.updateWorkers);
client.removeListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
client.mainRoot.off("processListChanged", this.updateWorkers);
client.mainRoot.off("serviceWorkerRegistrationListChanged", this.updateWorkers);
client.mainRoot.off("workerListChanged", this.updateWorkers);
client.removeListener("workerListChanged", this.updateWorkers);
client.removeListener("registration-changed", this.updateWorkers);

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

@ -83,15 +83,17 @@ add_task(async function reloadButtonReloadsAddon() {
});
const reloaded = once(AboutDebugging, "addon-reload");
const onListUpdated = once(AboutDebugging, "addons-updated");
reloadButton.click();
await reloaded;
await onListUpdated;
const [reloadedAddon] = await onInstalled;
is(reloadedAddon.name, ADDON_NAME,
"Add-on was reloaded: " + reloadedAddon.name);
await onBootstrapInstallCalled;
await tearDownAddon(reloadedAddon);
await tearDownAddon(AboutDebugging, reloadedAddon);
await closeAboutDebugging(tab);
});
@ -114,8 +116,12 @@ 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.
let onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
const onInstalled = promiseAddonEvent("onInstalled");
await AddonManager.installTemporaryAddon(tempExt.sourceDir);
await onListUpdated;
info("Wait until addon onInstalled event is received");
await onInstalled;
@ -126,12 +132,16 @@ 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
onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
// Wait for the add-on list to be updated with the reloaded name.
const onReInstall = promiseAddonEvent("onInstalled");
const reloadButton = getReloadButton(document, manifestBase.name);
const reloaded = once(AboutDebugging, "addon-reload");
reloadButton.click();
await reloaded;
await onListUpdated;
info("Wait until addon onInstalled event is received again");
const [reloadedAddon] = await onReInstall;
@ -139,15 +149,21 @@ add_task(async function reloadButtonRefreshesMetadata() {
info("Wait until addon name is updated in about:debugging#addons");
await waitUntilAddonContainer(newName, document);
await tearDownAddon(reloadedAddon);
await tearDownAddon(AboutDebugging, reloadedAddon);
tempExt.remove();
await closeAboutDebugging(tab);
});
add_task(async function onlyTempInstalledAddonsCanBeReloaded() {
const { tab, document } = await openAboutDebugging("addons");
const { tab, document, window } = await openAboutDebugging("addons");
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.
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
await onListUpdated;
info("Wait until addon appears in about:debugging#addons");
await waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
@ -159,6 +175,6 @@ add_task(async function onlyTempInstalledAddonsCanBeReloaded() {
const reloadButton = getReloadButton(document, addon.name);
ok(!reloadButton, "There should not be a reload button");
await tearDownAddon(addon);
await tearDownAddon(AboutDebugging, addon);
await closeAboutDebugging(tab);
});

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

@ -74,10 +74,15 @@ add_task(async function removeWebextension() {
});
add_task(async function onlyTempInstalledAddonsCanBeRemoved() {
const { tab, document } = await openAboutDebugging("addons");
const { tab, document, window } = await openAboutDebugging("addons");
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.
const onListUpdated = waitForNEvents(AboutDebugging, "addons-updated", 2);
await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
await onListUpdated;
const addon = await getAddonByID("bug1273184@tests");
info("Wait until addon appears in about:debugging#addons");
@ -86,6 +91,6 @@ add_task(async function onlyTempInstalledAddonsCanBeRemoved() {
const removeButton = getRemoveButton(document, addon.id);
ok(!removeButton, "remove button is not shown");
await tearDownAddon(addon);
await tearDownAddon(AboutDebugging, addon);
await closeAboutDebugging(tab);
});

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

@ -462,9 +462,11 @@ function getAddonByID(addonId) {
/**
* Uninstall an add-on.
*/
async function tearDownAddon(addon) {
async function tearDownAddon(AboutDebugging, addon) {
const onUninstalled = promiseAddonEvent("onUninstalled");
const onListUpdated = once(AboutDebugging, "addons-updated");
addon.uninstall();
await onListUpdated;
const [uninstalledAddon] = await onUninstalled;
is(uninstalledAddon.id, addon.id,
`Add-on was uninstalled: ${uninstalledAddon.id}`);

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

@ -33,6 +33,33 @@ class AccessibilityStartup {
return this._walker;
}
/**
* Determine which features are supported based on the version of the server. Also, sync
* the state of the accessibility front/actor.
* @return {Promise}
* A promise that returns true when accessibility front is fully in sync with
* the actor.
*/
async prepareAccessibility() {
// We must call a method on an accessibility front here (such as getWalker), in
// oreder to be able to check actor's backward compatibility via actorHasMethod.
// See targe.js@getActorDescription for more information.
this._walker = await this._accessibility.getWalker();
this._supports = {};
// Only works with FF61+ targets
this._supports.enableDisable =
await this.target.actorHasMethod("accessibility", "enable");
if (this._supports.enableDisable) {
this._supports.relations =
await this.target.actorHasMethod("accessible", "getRelations");
await this._accessibility.bootstrap();
}
return true;
}
/**
* Fully initialize accessibility front. Also add listeners for accessibility
* service lifecycle events that affect the state of the tool tab highlight.
@ -42,20 +69,22 @@ class AccessibilityStartup {
initAccessibility() {
if (!this._initAccessibility) {
this._initAccessibility = (async function() {
this._accessibility = this.target.getFront("accessibility");
// We must call a method on an accessibility front here (such as getWalker), in
// oreder to be able to check actor's backward compatibility via actorHasMethod.
// See targe.js@getActorDescription for more information.
this._walker = await this._accessibility.getWalker();
this._supports = {};
// Only works with FF61+ targets
this._supports.enableDisable =
await this.target.actorHasMethod("accessibility", "enable");
await Promise.race([
this.toolbox.isOpen,
this.toolbox.once("accessibility-init"),
]);
if (this._supports.enableDisable) {
this._supports.relations =
await this.target.actorHasMethod("accessible", "getRelations");
await this._accessibility.bootstrap();
this._accessibility = this.target.getFront("accessibility");
// When target is being destroyed (for example on remoteness change), it
// destroy accessibility front. In case when a11y is not fully initialized, that
// may result in unresolved promises.
const prepared = await Promise.race([
this.prepareAccessibility(),
this.target.once("close"), // does not have a value.
]);
// If the target is being destroyed, no need to continue.
if (!prepared) {
return;
}
this._updateToolHighlight();

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

@ -44,9 +44,9 @@ window.Application = {
},
};
this.toolbox.target.activeTab.on("workerListChanged", this.updateWorkers);
this.client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
this.client.mainRoot.on("serviceWorkerRegistrationListChanged", this.updateWorkers);
this.client.addListener("registration-changed", this.updateWorkers);
this.client.addListener("processListChanged", this.updateWorkers);
this.client.mainRoot.on("processListChanged", this.updateWorkers);
this.toolbox.target.on("navigate", this.updateDomain);
this.updateDomain();
@ -89,10 +89,10 @@ window.Application = {
destroy() {
this.toolbox.target.activeTab.off("workerListChanged", this.updateWorkers);
this.client.removeListener("serviceWorkerRegistrationListChanged",
this.client.mainRoot.off("serviceWorkerRegistrationListChanged",
this.updateWorkers);
this.client.removeListener("registration-changed", this.updateWorkers);
this.client.removeListener("processListChanged", this.updateWorkers);
this.client.mainRoot.off("processListChanged", this.updateWorkers);
this.toolbox.target.off("navigate", this.updateDomain);

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

@ -62,7 +62,7 @@ exports.targetFromURL = async function targetFromURL(url) {
const response = await client.getTab({ outerWindowID: id });
form = response.tab;
} catch (ex) {
if (ex.error == "noTab") {
if (ex.startsWith("Protocol error (noTab)")) {
throw new Error(`targetFromURL, tab with outerWindowID '${id}' doesn't exist`);
}
throw ex;

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

@ -12,7 +12,7 @@ var { WebAudioFront } =
async function testTarget(client, target) {
await target.attach();
is(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
is(target.hasActor("inspector"), true, "target.hasActor() true when actor exists.");
is(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
is(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
// Create a front to ensure the actor is loaded

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

@ -75,7 +75,7 @@ async function toggleJS(toolbox) {
let { javascriptEnabled } = toolbox.target.activeTab.configureOptions;
is(javascriptEnabled, !cbx.checked,
"BrowsingContextFront's configureOptions is correct before the toggle");
"BrowsingContextTargetFront's configureOptions is correct before the toggle");
const browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
cbx.click();
@ -83,7 +83,7 @@ async function toggleJS(toolbox) {
({ javascriptEnabled } = toolbox.target.activeTab.configureOptions);
is(javascriptEnabled, !cbx.checked,
"BrowsingContextFront's configureOptions is correctly updated");
"BrowsingContextTargetFront's configureOptions is correctly updated");
}
async function testJSDisabled() {

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

@ -108,6 +108,10 @@ function test() {
}
for (const actor of pool.__poolMap.keys()) {
// Ignore the root front as it is only release on client close
if (actor == "root") {
continue;
}
// Bug 1056342: Profiler fails today because of framerate actor, but
// this appears more complex to rework, so leave it for that bug to
// resolve.

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

@ -87,16 +87,14 @@ function checkGetTabFailures() {
.then(
response => ok(false, "getTab unexpectedly succeed with a wrong tabId"),
response => {
is(response.error, "noTab");
is(response.message, "Unable to find tab with tabId '-999'");
is(response, "Protocol error (noTab): Unable to find tab with tabId '-999'");
}
)
.then(() => gClient.getTab({ outerWindowID: -999 }))
.then(
response => ok(false, "getTab unexpectedly succeed with a wrong outerWindowID"),
response => {
is(response.error, "noTab");
is(response.message, "Unable to find tab with outerWindowID '-999'");
is(response, "Protocol error (noTab): Unable to find tab with outerWindowID '-999'");
}
)
.then(checkSelectedTargetActor);

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

@ -9,4 +9,4 @@ support-files =
# Each metrics tests is loaded in a separate .ini file. This way the test is executed
# individually, without any other test being executed before or after.
[browser_metrics_inspector.js]
skip-if = os != 'linux' || debug || asan # Results should be platform agnostic - only run on linux64-opt
skip-if = os != 'linux' || debug || asan || pgo # Results should be platform agnostic - only run on linux64-opt

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

@ -903,6 +903,10 @@ Inspector.prototype = {
layoutId,
layoutTitle,
{
props: {
id: layoutId,
title: layoutTitle,
},
panel: () => {
if (!this.layoutview) {
const LayoutView =
@ -927,6 +931,10 @@ Inspector.prototype = {
animationId,
animationTitle,
{
props: {
id: animationId,
title: animationTitle,
},
panel: () => {
const AnimationInspector =
this.browserRequire("devtools/client/inspector/animation/animation");
@ -944,6 +952,10 @@ Inspector.prototype = {
fontId,
fontTitle,
{
props: {
id: fontId,
title: fontTitle,
},
panel: () => {
if (!this.fontinspector) {
const FontInspector =
@ -965,6 +977,10 @@ Inspector.prototype = {
changesId,
changesTitle,
{
props: {
id: changesId,
title: changesTitle,
},
panel: () => {
if (!this.changesView) {
const ChangesView =

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

@ -21,7 +21,7 @@ pref("devtools.cache.disabled", false);
pref("devtools.netmonitor.enabled", true);
pref("devtools.netmonitor.filters", "[\"all\"]");
pref("devtools.netmonitor.visibleColumns",
"[\"status\",\"method\",\"file\",\"domain\",\"cause\"," +
"[\"status\",\"method\",\"domain,\"file\",\"domain\",\"cause\"," +
"\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
);
pref("devtools.netmonitor.panes-network-details-width", 550);

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

@ -1,7 +1,7 @@
<!-- 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/. -->
<svg width="16" height="16" fill="#0B0B0B" fill-rule="evenodd" xmlns="http://www.w3.org/2000/svg">
<svg width="16" height="16" fill="context-fill #0B0B0B" fill-rule="evenodd" xmlns="http://www.w3.org/2000/svg">
<path d="M3 14.001L2.996 14h9.008l-.004.002V6l.293.707-4-4L9 3H3.003L3 2.999V14zm-1 0V3C2 2.447 2.449 2 3.003 2H9l4 4v8.002a.996.996 0 0 1-.996.998H2.996A.996.996 0 0 1 2 14.001z"/>
<path d="M4.5 9H11V8H4v1h.5zM4.5 11H11v-1H4v1h.5zM4.5 13H11v-1H4v1h.5zM4.5 5H7V4H4v1h.5zM4.5 7H7V6H4v1h.5zM8 7h5V6H9V2H8v5z"/>
</svg>

До

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

После

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

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

@ -1,7 +1,7 @@
<!-- 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/. -->
<svg width="16" height="16" fill="#0B0B0B" fill-rule="evenodd" xmlns="http://www.w3.org/2000/svg">
<svg width="16" height="16" fill="context-fill #0B0B0B" fill-rule="evenodd" xmlns="http://www.w3.org/2000/svg">
<path d="M3 14.001L2.996 14h9.008l-.004.002V6l.293.707-4-4L9 3H3.003L3 2.999V14zm-1 0V3C2 2.447 2.449 2 3.003 2H9l4 4v8.002a.996.996 0 0 1-.996.998H2.996A.996.996 0 0 1 2 14.001z"/>
<path d="M8 7h5V6H9V2H8v5z"/>
</svg>

До

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

После

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

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

@ -256,6 +256,8 @@
height: 16px;
margin: 0 4px;
vertical-align: text-bottom;
-moz-context-properties: fill;
fill: currentColor;
}
.request-list-item.selected .requests-file-type-icon {

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

@ -32,7 +32,10 @@ class RequestListColumnFile extends Component {
} = this.props;
const iconClassList = ["requests-file-type-icon"];
if (cause && cause.type == "img") {
if (cause && (
cause.type.includes("img") ||
cause.type.includes("image") ||
cause.type.includes("beacon"))) {
iconClassList.push("file-type-image");
} else {
iconClassList.push("file-type-general");

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

@ -215,13 +215,13 @@ class RequestListItem extends Component {
},
columns.status && RequestListColumnStatus({ item }),
columns.method && RequestListColumnMethod({ item }),
columns.domain && RequestListColumnDomain({
item,
onSecurityIconMouseDown,
}),
columns.file && RequestListColumnFile({ item }),
columns.protocol && RequestListColumnProtocol({ item }),
columns.scheme && RequestListColumnScheme({ item }),
columns.domain && RequestListColumnDomain({
item,
onSecurityIconMouseDown,
}),
columns.remoteip && RequestListColumnRemoteIP({ item }),
columns.cause && RequestListColumnCause({
item,

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

@ -9,9 +9,6 @@ const { ACTIVITY_TYPE, EVENTS } = require("../constants");
const FirefoxDataProvider = require("./firefox-data-provider");
const { getDisplayedTimingMarker } = require("../selectors/index");
// To be removed once FF60 is deprecated
loader.lazyRequireGetter(this, "TimelineFront", "devtools/shared/fronts/timeline", true);
// Network throttling
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/components/throttling/profiles");
@ -26,7 +23,6 @@ class FirefoxConnector {
this.willNavigate = this.willNavigate.bind(this);
this.navigate = this.navigate.bind(this);
this.displayCachedEvents = this.displayCachedEvents.bind(this);
this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this);
this.onDocEvent = this.onDocEvent.bind(this);
this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this);
@ -126,33 +122,15 @@ class FirefoxConnector {
this.dataProvider.onNetworkEventUpdate);
this.webConsoleClient.on("documentEvent", this.onDocEvent);
// With FF60+ console actor supports listening to document events like
// DOMContentLoaded and load. We used to query Timeline actor, but it was too CPU
// intensive.
const { startedListeners } = await this.webConsoleClient.startListeners(
["DocumentEvents"]);
// Allows to know if we are on FF60 and support these events.
const supportsDocEvents = startedListeners.includes("DocumentEvents");
// Don't start up waiting for timeline markers if the server isn't
// recent enough (<FF45) to emit the markers we're interested in.
if (!supportsDocEvents && !this.timelineFront &&
this.tabTarget.getTrait("documentLoadingMarkers")) {
this.timelineFront = new TimelineFront(this.tabTarget.client, this.tabTarget.form);
this.timelineFront.on("doc-loading", this.onDocLoadingMarker);
await this.timelineFront.start({ withDocLoadingEvents: true });
}
// The console actor supports listening to document events like
// DOMContentLoaded and load.
await this.webConsoleClient.startListeners(["DocumentEvents"]);
}
async removeListeners() {
if (this.tabTarget) {
this.tabTarget.off("close");
}
if (this.timelineFront) {
this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
await this.timelineFront.destroy();
this.timelineFront = null;
}
if (this.webConsoleClient) {
this.webConsoleClient.off("networkEvent");
this.webConsoleClient.off("networkEventUpdate");
@ -231,34 +209,9 @@ class FirefoxConnector {
}
}
/**
* The "DOMContentLoaded" and "Load" events sent by the timeline actor.
*
* To be removed once FF60 is deprecated.
*
* @param {object} marker
*/
onDocLoadingMarker(marker) {
// Translate marker into event similar to newer "docEvent" event sent by the console
// actor
const event = {
name: marker.name == "document::DOMContentLoaded" ?
"dom-interactive" : "dom-complete",
time: marker.unixTime / 1000,
};
if (this.actions) {
this.actions.addTimingMarker(event);
}
this.emit(EVENTS.TIMELINE_EVENT, event);
}
/**
* The "DOMContentLoaded" and "Load" events sent by the console actor.
*
* Only used by FF60+.
*
* @param {object} marker
*/
onDocEvent(event) {

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

@ -183,6 +183,10 @@ const HEADERS = [
name: "method",
canFilter: true,
},
{
name: "domain",
canFilter: true,
},
{
name: "file",
canFilter: false,
@ -195,10 +199,6 @@ const HEADERS = [
name: "scheme",
canFilter: true,
},
{
name: "domain",
canFilter: true,
},
{
name: "remoteip",
canFilter: true,

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

@ -146,6 +146,7 @@ function requestsReducer(state = Requests(), action) {
...state,
requests: mapSet(requests, newRequest.id, newRequest),
selectedId: newRequest.id,
preselectedId: selectedId,
};
}

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

@ -26,10 +26,10 @@ const {
const cols = {
status: true,
method: true,
domain: true,
file: true,
protocol: false,
scheme: false,
domain: true,
remoteip: false,
cause: true,
type: true,
@ -156,6 +156,7 @@ function ui(state = UI(), action) {
case RESET_COLUMNS:
return resetColumns(state);
case REMOVE_SELECTED_CUSTOM_REQUEST:
return openNetworkDetails(state, { open: true });
case SEND_CUSTOM_REQUEST:
return openNetworkDetails(state, { open: false });
case SELECT_DETAILS_PANEL_TAB:

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

@ -165,12 +165,12 @@ function contentSize(first, second) {
const sorters = {
status,
method,
domain,
file,
protocol,
scheme,
cookies,
setCookies,
domain,
remoteip,
cause,
type,

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

@ -120,6 +120,7 @@ subsuite = clipboard
[browser_net_frame.js]
skip-if = (os == 'mac') # Bug 1479782
[browser_net_header-docs.js]
[browser_net_edit_resend_cancel.js]
[browser_net_edit_resend_caret.js]
[browser_net_edit_resend_with_filtering.js]
[browser_net_edit_resend_xhr.js]

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

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if original request's header panel is visible when custom request is cancelled.
*/
add_task(async function() {
const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
info("Starting test... ");
const { document, store, windowRequire, parent } = monitor.panelWin;
const {
getSelectedRequest,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
const parentDocument = parent.document;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
// Reload to have one request in the list
const waitForEvents = waitForNetworkEvents(monitor, 1);
BrowserTestUtils.loadURI(tab.linkedBrowser, SIMPLE_URL);
await waitForEvents;
// Context Menu > "Edit & Resend"
const firstRequest = document.querySelectorAll(".request-list-item")[0];
const waitForHeaders = waitUntil(() => document.querySelector(".headers-overview"));
EventUtils.sendMouseEvent({ type: "mousedown" }, firstRequest);
await waitForHeaders;
EventUtils.sendMouseEvent({ type: "contextmenu" }, firstRequest);
const firstRequestState = getSelectedRequest(store.getState());
const contextResend = parentDocument.querySelector("#request-list-context-resend");
contextResend.click();
// Waits for "Edit & Resend" panel to appear > New request "Cancel"
document.querySelector("#custom-request-close-button").click();
const finalRequestState = getSelectedRequest(store.getState());
ok(firstRequestState === finalRequestState,
"Original request is selected after cancel button is clicked"
);
ok(document.querySelector(".headers-overview") !== null,
"Request is selected and headers panel is visible"
);
return teardown(monitor);
});

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

@ -86,7 +86,6 @@ const webpackConfig = {
"devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
"devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
"devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
"devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
"devtools/client/netmonitor/src/utils/firefox/open-request-in-tab": path.join(__dirname, "src/utils/open-request-in-tab"),
"devtools/client/shared/unicode-url": "./node_modules/devtools-modules/src/unicode-url",

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

@ -164,7 +164,7 @@ pref("devtools.netmonitor.panes-network-details-width", 550);
pref("devtools.netmonitor.panes-network-details-height", 450);
pref("devtools.netmonitor.filters", "[\"all\"]");
pref("devtools.netmonitor.visibleColumns",
"[\"status\",\"method\",\"file\",\"domain\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
"[\"status\",\"method\",\"domain\",\"file\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
);
// Save request/response bodies yes/no.

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

@ -317,9 +317,6 @@ class Tabbar extends Component {
});
}
// Ensure tab id and title are used as fallbacks if not defined as panel props.
tab.panel.props = Object.assign({ id: tab.id, title: tab.title }, tab.panel.props);
return tab.panel;
}

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

@ -48,7 +48,7 @@ function test() {
function testFirstAddon() {
let addonListChanged = false;
gClient.addOneTimeListener("addonListChanged", () => {
gClient.mainRoot.once("addonListChanged").then(() => {
addonListChanged = true;
});
@ -65,7 +65,7 @@ function testFirstAddon() {
function testSecondAddon() {
let addonListChanged = false;
gClient.addOneTimeListener("addonListChanged", function() {
gClient.mainRoot.once("addonListChanged").then(() => {
addonListChanged = true;
});
@ -84,7 +84,7 @@ function testSecondAddon() {
function testRemoveFirstAddon() {
let addonListChanged = false;
gClient.addOneTimeListener("addonListChanged", function() {
gClient.mainRoot.once("addonListChanged").then(() => {
addonListChanged = true;
});
@ -98,7 +98,7 @@ function testRemoveFirstAddon() {
function testRemoveSecondAddon() {
let addonListChanged = false;
gClient.addOneTimeListener("addonListChanged", function() {
gClient.mainRoot.once("addonListChanged").then(() => {
addonListChanged = true;
});

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

@ -1,16 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// MOCK FOR TimelineFront
class TimelineFront {
constructor(client, { timelineActor }) {}
start({ withDocLoadingEvents }) {}
destroy() {}
on(evt, cb) {}
off(evt, cb) {}
}
exports.TimelineFront = TimelineFront;

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

@ -59,13 +59,13 @@ TabStore.prototype = {
_onStatusChanged: function() {
if (this._connection.status == Connection.Status.CONNECTED) {
// Watch for changes to remote browser tabs
this._connection.client.addListener("tabListChanged",
this._connection.client.mainRoot.on("tabListChanged",
this._onTabListChanged);
this.listTabs();
} else {
if (this._connection.client) {
this._connection.client.removeListener("tabListChanged",
this._onTabListChanged);
this._connection.client.mainRoot.off("tabListChanged",
this._onTabListChanged);
}
this._resetStore();
}

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

@ -0,0 +1,278 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { DebuggerServer } = require("devtools/server/main");
const Services = require("Services");
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const defer = require("devtools/shared/defer");
const { accessibilitySpec } = require("devtools/shared/specs/accessibility");
loader.lazyRequireGetter(this, "AccessibleWalkerActor", "devtools/server/actors/accessibility/walker", true);
loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter");
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
/**
* The AccessibilityActor is a top level container actor that initializes
* accessible walker and is the top-most point of interaction for accessibility
* tools UI.
*/
const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, {
initialize(conn, targetActor) {
Actor.prototype.initialize.call(this, conn);
this.initializedDeferred = defer();
if (DebuggerServer.isInChildProcess) {
this._msgName = `debug:${this.conn.prefix}accessibility`;
this.conn.setupInParent({
module: "devtools/server/actors/accessibility/accessibility-parent",
setupParent: "setupParentProcess",
});
this.onMessage = this.onMessage.bind(this);
this.messageManager.addMessageListener(`${this._msgName}:event`, this.onMessage);
} else {
this.userPref = Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
Services.obs.addObserver(this, "a11y-consumers-changed");
Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
this.initializedDeferred.resolve();
}
Services.obs.addObserver(this, "a11y-init-or-shutdown");
this.targetActor = targetActor;
},
bootstrap() {
return this.initializedDeferred.promise.then(() => ({
enabled: this.enabled,
canBeEnabled: this.canBeEnabled,
canBeDisabled: this.canBeDisabled,
}));
},
get enabled() {
return Services.appinfo.accessibilityEnabled;
},
get canBeEnabled() {
if (DebuggerServer.isInChildProcess) {
return this._canBeEnabled;
}
return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1;
},
get canBeDisabled() {
if (DebuggerServer.isInChildProcess) {
return this._canBeDisabled;
} else if (!this.enabled) {
return true;
}
const { PlatformAPI } = JSON.parse(this.walker.a11yService.getConsumers());
return !PlatformAPI;
},
/**
* Getter for a message manager that corresponds to a current tab. It is onyl
* used if the AccessibilityActor runs in the child process.
*
* @return {Object}
* Message manager that corresponds to the current content tab.
*/
get messageManager() {
if (!DebuggerServer.isInChildProcess) {
throw new Error(
"Message manager should only be used when actor is in child process.");
}
return this.conn.parentMessageManager;
},
onMessage(msg) {
const { topic, data } = msg.data;
switch (topic) {
case "initialized":
this._canBeEnabled = data.canBeEnabled;
this._canBeDisabled = data.canBeDisabled;
// Sometimes when the tool is reopened content process accessibility service is
// not shut down yet because GC did not run in that process (though it did in
// parent process and the service was shut down there). We need to sync the two
// services if possible.
if (!data.enabled && this.enabled && data.canBeEnabled) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
}
this.initializedDeferred.resolve();
break;
case "can-be-disabled-change":
this._canBeDisabled = data;
events.emit(this, "can-be-disabled-change", this.canBeDisabled);
break;
case "can-be-enabled-change":
this._canBeEnabled = data;
events.emit(this, "can-be-enabled-change", this.canBeEnabled);
break;
default:
break;
}
},
/**
* Enable acessibility service in the given process.
*/
async enable() {
if (this.enabled || !this.canBeEnabled) {
return;
}
const initPromise = this.once("init");
if (DebuggerServer.isInChildProcess) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
} else {
// This executes accessibility service lazy getter and adds accessible
// events observer.
this.walker.a11yService;
}
await initPromise;
},
/**
* Disable acessibility service in the given process.
*/
async disable() {
if (!this.enabled || !this.canBeDisabled) {
return;
}
this.disabling = true;
const shutdownPromise = this.once("shutdown");
if (DebuggerServer.isInChildProcess) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "disable" });
} else {
// Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable
// accessibility service. This is the only way to guarantee an immediate
// accessibility service shutdown in all processes. This also prevents
// accessibility service from starting up in the future.
//
// TODO: Introduce a shutdown method that is exposed via XPCOM on
// accessibility service.
Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
// Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user
// set value. This will not start accessibility service until the user
// activates it again. It simply ensures that accessibility service can
// start again (when value is below 1).
Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref);
}
await shutdownPromise;
delete this.disabling;
},
/**
* Observe Accessibility service init and shutdown events. It relays these
* events to AccessibilityFront iff the event is fired for the a11y service
* that lives in the same process.
*
* @param {null} subject
* Not used.
* @param {String} topic
* Name of the a11y service event: "a11y-init-or-shutdown".
* @param {String} data
* "0" corresponds to shutdown and "1" to init.
*/
observe(subject, topic, data) {
if (topic === "a11y-init-or-shutdown") {
// This event is fired when accessibility service is initialized or shut
// down. "init" and "shutdown" events are only relayed when the enabled
// state matches the event (e.g. the event came from the same process as
// the actor).
const enabled = data === "1";
if (enabled && this.enabled) {
events.emit(this, "init");
} else if (!enabled && !this.enabled) {
if (this.walker) {
this.walker.reset();
}
events.emit(this, "shutdown");
}
} else if (topic === "a11y-consumers-changed") {
// This event is fired when accessibility service consumers change. There
// are 3 possible consumers of a11y service: XPCOM, PlatformAPI (e.g.
// screen readers) and MainProcess. PlatformAPI consumer can only be set
// in parent process, and MainProcess consumer can only be set in child
// process. We only care about PlatformAPI consumer changes because when
// set, we can no longer disable accessibility service.
const { PlatformAPI } = JSON.parse(data);
events.emit(this, "can-be-disabled-change", !PlatformAPI);
} else if (!this.disabling && topic === "nsPref:changed" &&
data === PREF_ACCESSIBILITY_FORCE_DISABLED) {
// PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to
// >=1, it means that the user wants to disable accessibility service and
// prevent it from starting in the future. Note: we also check
// this.disabling state when handling this pref change because this is how
// we disable the accessibility inspector itself.
events.emit(this, "can-be-enabled-change", this.canBeEnabled);
}
},
/**
* Get or create AccessibilityWalker actor, similar to WalkerActor.
*
* @return {Object}
* AccessibleWalkerActor for the current tab.
*/
getWalker() {
if (!this.walker) {
this.walker = new AccessibleWalkerActor(this.conn, this.targetActor);
}
return this.walker;
},
/**
* Destroy accessibility service actor. This method also shutsdown
* accessibility service if possible.
*/
async destroy() {
if (this.destroyed) {
await this.destroyed;
return;
}
let resolver;
this.destroyed = new Promise(resolve => {
resolver = resolve;
});
if (this.walker) {
this.walker.reset();
}
Services.obs.removeObserver(this, "a11y-init-or-shutdown");
if (DebuggerServer.isInChildProcess) {
this.messageManager.removeMessageListener(`${this._msgName}:event`,
this.onMessage);
} else {
Services.obs.removeObserver(this, "a11y-consumers-changed");
Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
}
Actor.prototype.destroy.call(this);
this.walker = null;
this.targetActor = null;
resolver();
},
});
exports.AccessibilityActor = AccessibilityActor;

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

@ -0,0 +1,314 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci } = 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);
const nsIAccessibleRelation = Ci.nsIAccessibleRelation;
const RELATIONS_TO_IGNORE = new Set([
nsIAccessibleRelation.RELATION_CONTAINING_APPLICATION,
nsIAccessibleRelation.RELATION_CONTAINING_TAB_PANE,
nsIAccessibleRelation.RELATION_CONTAINING_WINDOW,
nsIAccessibleRelation.RELATION_PARENT_WINDOW_OF,
nsIAccessibleRelation.RELATION_SUBWINDOW_OF,
]);
/**
* The AccessibleActor provides information about a given accessible object: its
* role, name, states, etc.
*/
const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
initialize(walker, rawAccessible) {
Actor.prototype.initialize.call(this, walker.conn);
this.walker = walker;
this.rawAccessible = rawAccessible;
/**
* Indicates if the raw accessible is no longer alive.
*
* @return Boolean
*/
Object.defineProperty(this, "isDefunct", {
get() {
const defunct = isDefunct(this.rawAccessible);
if (defunct) {
delete this.isDefunct;
this.isDefunct = true;
return this.isDefunct;
}
return defunct;
},
configurable: true,
});
},
/**
* Items returned by this actor should belong to the parent walker.
*/
marshallPool() {
return this.walker;
},
destroy() {
Actor.prototype.destroy.call(this);
this.walker = null;
this.rawAccessible = null;
},
get role() {
if (this.isDefunct) {
return null;
}
return this.walker.a11yService.getStringRole(this.rawAccessible.role);
},
get name() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.name;
},
get value() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.value;
},
get description() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.description;
},
get keyboardShortcut() {
if (this.isDefunct) {
return null;
}
// Gecko accessibility exposes two key bindings: Accessible::AccessKey and
// Accessible::KeyboardShortcut. The former is used for accesskey, where the latter
// is used for global shortcuts defined by XUL menu items, etc. Here - do what the
// Windows implementation does: try AccessKey first, and if that's empty, use
// KeyboardShortcut.
return this.rawAccessible.accessKey || this.rawAccessible.keyboardShortcut;
},
get childCount() {
if (this.isDefunct) {
return 0;
}
return this.rawAccessible.childCount;
},
get domNodeType() {
if (this.isDefunct) {
return 0;
}
return this.rawAccessible.DOMNode ? this.rawAccessible.DOMNode.nodeType : 0;
},
get parentAcc() {
if (this.isDefunct) {
return null;
}
return this.walker.addRef(this.rawAccessible.parent);
},
children() {
const children = [];
if (this.isDefunct) {
return children;
}
for (let child = this.rawAccessible.firstChild; child; child = child.nextSibling) {
children.push(this.walker.addRef(child));
}
return children;
},
get indexInParent() {
if (this.isDefunct) {
return -1;
}
try {
return this.rawAccessible.indexInParent;
} catch (e) {
// Accessible is dead.
return -1;
}
},
get actions() {
const actions = [];
if (this.isDefunct) {
return actions;
}
for (let i = 0; i < this.rawAccessible.actionCount; i++) {
actions.push(this.rawAccessible.getActionDescription(i));
}
return actions;
},
get states() {
if (this.isDefunct) {
return [];
}
const state = {};
const extState = {};
this.rawAccessible.getState(state, extState);
return [
...this.walker.a11yService.getStringStates(state.value, extState.value),
];
},
get attributes() {
if (this.isDefunct || !this.rawAccessible.attributes) {
return {};
}
const attributes = {};
for (const { key, value } of this.rawAccessible.attributes.enumerate()) {
attributes[key] = value;
}
return attributes;
},
get bounds() {
if (this.isDefunct) {
return null;
}
let x = {}, y = {}, w = {}, h = {};
try {
this.rawAccessible.getBoundsInCSSPixels(x, y, w, h);
x = x.value;
y = y.value;
w = w.value;
h = h.value;
} catch (e) {
return null;
}
// Check if accessible bounds are invalid.
const left = x, right = x + w, top = y, bottom = y + h;
if (left === right || top === bottom) {
return null;
}
return { x, y, w, h };
},
async getRelations() {
const relationObjects = [];
if (this.isDefunct) {
return relationObjects;
}
const relations =
[...this.rawAccessible.getRelations().enumerate(nsIAccessibleRelation)];
if (relations.length === 0) {
return relationObjects;
}
const doc = await this.walker.getDocument();
relations.forEach(relation => {
if (RELATIONS_TO_IGNORE.has(relation.relationType)) {
return;
}
const type = this.walker.a11yService.getStringRelationType(relation.relationType);
const targets = [...relation.getTargets().enumerate(Ci.nsIAccessible)];
let relationObject;
for (const target of targets) {
// Target of the relation is not part of the current root document.
if (target.rootDocument !== doc.rawAccessible) {
continue;
}
let targetAcc;
try {
targetAcc = this.walker.attachAccessible(target, doc);
} catch (e) {
// Target is not available.
}
if (targetAcc) {
if (!relationObject) {
relationObject = { type, targets: [] };
}
relationObject.targets.push(targetAcc);
}
}
if (relationObject) {
relationObjects.push(relationObject);
}
});
return relationObjects;
},
form() {
return {
actor: this.actorID,
role: this.role,
name: this.name,
value: this.value,
description: this.description,
keyboardShortcut: this.keyboardShortcut,
childCount: this.childCount,
domNodeType: this.domNodeType,
indexInParent: this.indexInParent,
states: this.states,
actions: this.actions,
attributes: this.attributes,
};
},
_isValidTextLeaf(rawAccessible) {
return !isDefunct(rawAccessible) &&
rawAccessible.role === Ci.nsIAccessibleRole.ROLE_TEXT_LEAF &&
rawAccessible.name && rawAccessible.name.trim().length > 0;
},
get _nonEmptyTextLeafs() {
return this.children().filter(child => this._isValidTextLeaf(child.rawAccessible));
},
/**
* Calculate the contrast ratio of the given accessible.
*/
_getContrastRatio() {
return getContrastRatioFor(this._isValidTextLeaf(this.rawAccessible) ?
this.rawAccessible.DOMNode.parentNode : this.rawAccessible.DOMNode);
},
/**
* Audit the state of the accessible object.
*
* @return {Object|null}
* Audit results for the accessible object.
*/
get audit() {
return this.isDefunct ? null : {
contrastRatio: this._getContrastRatio(),
};
},
});
exports.AccessibleActor = AccessibleActor;

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

@ -0,0 +1,13 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'accessibility-parent.js',
'accessibility.js',
'accessible.js',
'walker.js',
)
with Files('**'):
BUG_COMPONENT = ('DevTools', 'Accessibility Tools')

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

@ -5,38 +5,24 @@
"use strict";
const { Cc, Ci } = require("chrome");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DebuggerServer } = require("devtools/server/main");
const Services = require("Services");
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const defer = require("devtools/shared/defer");
const events = require("devtools/shared/event-emitter");
const {
accessibleSpec,
accessibleWalkerSpec,
accessibilitySpec,
} = require("devtools/shared/specs/accessibility");
const { accessibleWalkerSpec } = require("devtools/shared/specs/accessibility");
const { isXUL } = require("devtools/server/actors/highlighters/utils/markup");
const { isWindowIncluded } = require("devtools/shared/layout/utils");
const { CustomHighlighterActor, register } =
require("devtools/server/actors/highlighters");
const { getContrastRatioFor } = require("devtools/server/actors/utils/accessibility");
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
loader.lazyRequireGetter(this, "AccessibleActor", "devtools/server/actors/accessibility/accessible", true);
loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "isDefunct", "devtools/server/actors/utils/accessibility", true);
loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
loader.lazyRequireGetter(this, "isWindowIncluded", "devtools/shared/layout/utils", true);
loader.lazyRequireGetter(this, "isXUL", "devtools/server/actors/highlighters/utils/markup", true);
loader.lazyRequireGetter(this, "register", "devtools/server/actors/highlighters", true);
const nsIAccessibleEvent = Ci.nsIAccessibleEvent;
const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent;
const nsIAccessibleRelation = Ci.nsIAccessibleRelation;
const nsIAccessibleRole = Ci.nsIAccessibleRole;
const RELATIONS_TO_IGNORE = new Set([
nsIAccessibleRelation.RELATION_CONTAINING_APPLICATION,
nsIAccessibleRelation.RELATION_CONTAINING_TAB_PANE,
nsIAccessibleRelation.RELATION_CONTAINING_WINDOW,
nsIAccessibleRelation.RELATION_PARENT_WINDOW_OF,
nsIAccessibleRelation.RELATION_SUBWINDOW_OF,
]);
const {
EVENT_TEXT_CHANGED,
EVENT_TEXT_INSERTED,
@ -107,39 +93,6 @@ const NAME_FROM_SUBTREE_RULE_ROLES = new Set([
const IS_OSX = Services.appinfo.OS === "Darwin";
register("AccessibleHighlighter", "accessible");
register("XULWindowAccessibleHighlighter", "xul-accessible");
/**
* Helper function that determines if nsIAccessible object is in defunct state.
*
* @param {nsIAccessible} accessible
* object to be tested.
* @return {Boolean}
* True if accessible object is defunct, false otherwise.
*/
function isDefunct(accessible) {
// If accessibility is disabled, safely assume that the accessible object is
// now dead.
if (!Services.appinfo.accessibilityEnabled) {
return true;
}
let defunct = false;
try {
const extraState = {};
accessible.getState({}, extraState);
// extraState.value is a bitmask. We are applying bitwise AND to mask out
// irrelevant states.
defunct = !!(extraState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
} catch (e) {
defunct = true;
}
return defunct;
}
/**
* Helper function that determines if nsIAccessible object is in stale state. When an
* object is stale it means its subtree is not up to date.
@ -157,311 +110,6 @@ function isStale(accessible) {
return !!(extraState.value & Ci.nsIAccessibleStates.EXT_STATE_STALE);
}
/**
* Set of actors that expose accessibility tree information to the
* devtools protocol clients.
*
* The |Accessibility| actor is the main entry point. It is used to request
* an AccessibleWalker actor that caches the tree of Accessible actors.
*
* The |AccessibleWalker| actor is used to cache all seen Accessible actors as
* well as observe all relevant accessible events.
*
* The |Accessible| actor provides information about a particular accessible
* object, its properties, , attributes, states, relations, etc.
*/
/**
* The AccessibleActor provides information about a given accessible object: its
* role, name, states, etc.
*/
const AccessibleActor = ActorClassWithSpec(accessibleSpec, {
initialize(walker, rawAccessible) {
Actor.prototype.initialize.call(this, walker.conn);
this.walker = walker;
this.rawAccessible = rawAccessible;
/**
* Indicates if the raw accessible is no longer alive.
*
* @return Boolean
*/
Object.defineProperty(this, "isDefunct", {
get() {
const defunct = isDefunct(this.rawAccessible);
if (defunct) {
delete this.isDefunct;
this.isDefunct = true;
return this.isDefunct;
}
return defunct;
},
configurable: true,
});
},
/**
* Items returned by this actor should belong to the parent walker.
*/
marshallPool() {
return this.walker;
},
destroy() {
Actor.prototype.destroy.call(this);
this.walker = null;
this.rawAccessible = null;
},
get role() {
if (this.isDefunct) {
return null;
}
return this.walker.a11yService.getStringRole(this.rawAccessible.role);
},
get name() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.name;
},
get value() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.value;
},
get description() {
if (this.isDefunct) {
return null;
}
return this.rawAccessible.description;
},
get keyboardShortcut() {
if (this.isDefunct) {
return null;
}
// Gecko accessibility exposes two key bindings: Accessible::AccessKey and
// Accessible::KeyboardShortcut. The former is used for accesskey, where the latter
// is used for global shortcuts defined by XUL menu items, etc. Here - do what the
// Windows implementation does: try AccessKey first, and if that's empty, use
// KeyboardShortcut.
return this.rawAccessible.accessKey || this.rawAccessible.keyboardShortcut;
},
get childCount() {
if (this.isDefunct) {
return 0;
}
return this.rawAccessible.childCount;
},
get domNodeType() {
if (this.isDefunct) {
return 0;
}
return this.rawAccessible.DOMNode ? this.rawAccessible.DOMNode.nodeType : 0;
},
get parentAcc() {
if (this.isDefunct) {
return null;
}
return this.walker.addRef(this.rawAccessible.parent);
},
children() {
const children = [];
if (this.isDefunct) {
return children;
}
for (let child = this.rawAccessible.firstChild; child; child = child.nextSibling) {
children.push(this.walker.addRef(child));
}
return children;
},
get indexInParent() {
if (this.isDefunct) {
return -1;
}
try {
return this.rawAccessible.indexInParent;
} catch (e) {
// Accessible is dead.
return -1;
}
},
get actions() {
const actions = [];
if (this.isDefunct) {
return actions;
}
for (let i = 0; i < this.rawAccessible.actionCount; i++) {
actions.push(this.rawAccessible.getActionDescription(i));
}
return actions;
},
get states() {
if (this.isDefunct) {
return [];
}
const state = {};
const extState = {};
this.rawAccessible.getState(state, extState);
return [
...this.walker.a11yService.getStringStates(state.value, extState.value),
];
},
get attributes() {
if (this.isDefunct || !this.rawAccessible.attributes) {
return {};
}
const attributes = {};
for (const { key, value } of this.rawAccessible.attributes.enumerate()) {
attributes[key] = value;
}
return attributes;
},
get bounds() {
if (this.isDefunct) {
return null;
}
let x = {}, y = {}, w = {}, h = {};
try {
this.rawAccessible.getBoundsInCSSPixels(x, y, w, h);
x = x.value;
y = y.value;
w = w.value;
h = h.value;
} catch (e) {
return null;
}
// Check if accessible bounds are invalid.
const left = x, right = x + w, top = y, bottom = y + h;
if (left === right || top === bottom) {
return null;
}
return { x, y, w, h };
},
async getRelations() {
const relationObjects = [];
if (this.isDefunct) {
return relationObjects;
}
const relations =
[...this.rawAccessible.getRelations().enumerate(nsIAccessibleRelation)];
if (relations.length === 0) {
return relationObjects;
}
const doc = await this.walker.getDocument();
relations.forEach(relation => {
if (RELATIONS_TO_IGNORE.has(relation.relationType)) {
return;
}
const type = this.walker.a11yService.getStringRelationType(relation.relationType);
const targets = [...relation.getTargets().enumerate(Ci.nsIAccessible)];
let relationObject;
for (const target of targets) {
// Target of the relation is not part of the current root document.
if (target.rootDocument !== doc.rawAccessible) {
continue;
}
let targetAcc;
try {
targetAcc = this.walker.attachAccessible(target, doc);
} catch (e) {
// Target is not available.
}
if (targetAcc) {
if (!relationObject) {
relationObject = { type, targets: [] };
}
relationObject.targets.push(targetAcc);
}
}
if (relationObject) {
relationObjects.push(relationObject);
}
});
return relationObjects;
},
form() {
return {
actor: this.actorID,
role: this.role,
name: this.name,
value: this.value,
description: this.description,
keyboardShortcut: this.keyboardShortcut,
childCount: this.childCount,
domNodeType: this.domNodeType,
indexInParent: this.indexInParent,
states: this.states,
actions: this.actions,
attributes: this.attributes,
};
},
_isValidTextLeaf(rawAccessible) {
return !isDefunct(rawAccessible) &&
rawAccessible.role === nsIAccessibleRole.ROLE_TEXT_LEAF &&
rawAccessible.name && rawAccessible.name.trim().length > 0;
},
get _nonEmptyTextLeafs() {
return this.children().filter(child => this._isValidTextLeaf(child.rawAccessible));
},
/**
* Calculate the contrast ratio of the given accessible.
*/
_getContrastRatio() {
return getContrastRatioFor(this._isValidTextLeaf(this.rawAccessible) ?
this.rawAccessible.DOMNode.parentNode : this.rawAccessible.DOMNode);
},
/**
* Audit the state of the accessible object.
*
* @return {Object|null}
* Audit results for the accessible object.
*/
get audit() {
return this.isDefunct ? null : {
contrastRatio: this._getContrastRatio(),
};
},
});
/**
* The AccessibleWalkerActor stores a cache of AccessibleActors that represent
* accessible objects in a given document.
@ -480,12 +128,30 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
this.onHovered = this.onHovered.bind(this);
this.onKey = this.onKey.bind(this);
this.onHighlighterEvent = this.onHighlighterEvent.bind(this);
},
this.highlighter = CustomHighlighterActor(this, isXUL(this.rootWin) ?
"XULWindowAccessibleHighlighter" : "AccessibleHighlighter");
get highlighter() {
if (!this._highlighter) {
if (isXUL(this.rootWin)) {
if (!isTypeRegistered("XULWindowAccessibleHighlighter")) {
register("XULWindowAccessibleHighlighter", "xul-accessible");
}
this.manage(this.highlighter);
this.highlighter.on("highlighter-event", this.onHighlighterEvent);
this._highlighter = CustomHighlighterActor(this,
"XULWindowAccessibleHighlighter");
} else {
if (!isTypeRegistered("AccessibleHighlighter")) {
register("AccessibleHighlighter", "accessible");
}
this._highlighter = CustomHighlighterActor(this, "AccessibleHighlighter");
}
this.manage(this._highlighter);
this._highlighter.on("highlighter-event", this.onHighlighterEvent);
}
return this._highlighter;
},
setA11yServiceGetter() {
@ -538,8 +204,10 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
this.reset();
this.highlighter.off("highlighter-event", this.onHighlighterEvent);
this.highlighter = null;
if (this._highlighter) {
this._highlighter.off("highlighter-event", this.onHighlighterEvent);
this._highlighter = null;
}
this.targetActor = null;
this.refMap = null;
@ -820,6 +488,10 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
* side.
*/
unhighlight() {
if (!this._highlighter) {
return;
}
this.highlighter.hide();
},
@ -1051,7 +723,9 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
* Cacncel picker pick. Remvoe all content listeners and hide the highlighter.
*/
cancelPick: function() {
this.highlighter.hide();
if (this._highlighter) {
this.highlighter.hide();
}
if (this._isPicking) {
this._stopPickerListeners();
@ -1061,266 +735,4 @@ const AccessibleWalkerActor = ActorClassWithSpec(accessibleWalkerSpec, {
},
});
/**
* The AccessibilityActor is a top level container actor that initializes
* accessible walker and is the top-most point of interaction for accessibility
* tools UI.
*/
const AccessibilityActor = ActorClassWithSpec(accessibilitySpec, {
initialize(conn, targetActor) {
Actor.prototype.initialize.call(this, conn);
this.initializedDeferred = defer();
if (DebuggerServer.isInChildProcess) {
this._msgName = `debug:${this.conn.prefix}accessibility`;
this.conn.setupInParent({
module: "devtools/server/actors/accessibility-parent",
setupParent: "setupParentProcess",
});
this.onMessage = this.onMessage.bind(this);
this.messageManager.addMessageListener(`${this._msgName}:event`, this.onMessage);
} else {
this.userPref = Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
Services.obs.addObserver(this, "a11y-consumers-changed");
Services.prefs.addObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
this.initializedDeferred.resolve();
}
Services.obs.addObserver(this, "a11y-init-or-shutdown");
this.targetActor = targetActor;
},
bootstrap() {
return this.initializedDeferred.promise.then(() => ({
enabled: this.enabled,
canBeEnabled: this.canBeEnabled,
canBeDisabled: this.canBeDisabled,
}));
},
get enabled() {
return Services.appinfo.accessibilityEnabled;
},
get canBeEnabled() {
if (DebuggerServer.isInChildProcess) {
return this._canBeEnabled;
}
return Services.prefs.getIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED) < 1;
},
get canBeDisabled() {
if (DebuggerServer.isInChildProcess) {
return this._canBeDisabled;
} else if (!this.enabled) {
return true;
}
const { PlatformAPI } = JSON.parse(this.walker.a11yService.getConsumers());
return !PlatformAPI;
},
/**
* Getter for a message manager that corresponds to a current tab. It is onyl
* used if the AccessibilityActor runs in the child process.
*
* @return {Object}
* Message manager that corresponds to the current content tab.
*/
get messageManager() {
if (!DebuggerServer.isInChildProcess) {
throw new Error(
"Message manager should only be used when actor is in child process.");
}
return this.conn.parentMessageManager;
},
onMessage(msg) {
const { topic, data } = msg.data;
switch (topic) {
case "initialized":
this._canBeEnabled = data.canBeEnabled;
this._canBeDisabled = data.canBeDisabled;
// Sometimes when the tool is reopened content process accessibility service is
// not shut down yet because GC did not run in that process (though it did in
// parent process and the service was shut down there). We need to sync the two
// services if possible.
if (!data.enabled && this.enabled && data.canBeEnabled) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
}
this.initializedDeferred.resolve();
break;
case "can-be-disabled-change":
this._canBeDisabled = data;
events.emit(this, "can-be-disabled-change", this.canBeDisabled);
break;
case "can-be-enabled-change":
this._canBeEnabled = data;
events.emit(this, "can-be-enabled-change", this.canBeEnabled);
break;
default:
break;
}
},
/**
* Enable acessibility service in the given process.
*/
async enable() {
if (this.enabled || !this.canBeEnabled) {
return;
}
const initPromise = this.once("init");
if (DebuggerServer.isInChildProcess) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "enable" });
} else {
// This executes accessibility service lazy getter and adds accessible
// events observer.
this.walker.a11yService;
}
await initPromise;
},
/**
* Disable acessibility service in the given process.
*/
async disable() {
if (!this.enabled || !this.canBeDisabled) {
return;
}
this.disabling = true;
const shutdownPromise = this.once("shutdown");
if (DebuggerServer.isInChildProcess) {
this.messageManager.sendAsyncMessage(this._msgName, { action: "disable" });
} else {
// Set PREF_ACCESSIBILITY_FORCE_DISABLED to 1 to force disable
// accessibility service. This is the only way to guarantee an immediate
// accessibility service shutdown in all processes. This also prevents
// accessibility service from starting up in the future.
//
// TODO: Introduce a shutdown method that is exposed via XPCOM on
// accessibility service.
Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
// Set PREF_ACCESSIBILITY_FORCE_DISABLED back to previous default or user
// set value. This will not start accessibility service until the user
// activates it again. It simply ensures that accessibility service can
// start again (when value is below 1).
Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, this.userPref);
}
await shutdownPromise;
delete this.disabling;
},
/**
* Observe Accessibility service init and shutdown events. It relays these
* events to AccessibilityFront iff the event is fired for the a11y service
* that lives in the same process.
*
* @param {null} subject
* Not used.
* @param {String} topic
* Name of the a11y service event: "a11y-init-or-shutdown".
* @param {String} data
* "0" corresponds to shutdown and "1" to init.
*/
observe(subject, topic, data) {
if (topic === "a11y-init-or-shutdown") {
// This event is fired when accessibility service is initialized or shut
// down. "init" and "shutdown" events are only relayed when the enabled
// state matches the event (e.g. the event came from the same process as
// the actor).
const enabled = data === "1";
if (enabled && this.enabled) {
events.emit(this, "init");
} else if (!enabled && !this.enabled) {
if (this.walker) {
this.walker.reset();
}
events.emit(this, "shutdown");
}
} else if (topic === "a11y-consumers-changed") {
// This event is fired when accessibility service consumers change. There
// are 3 possible consumers of a11y service: XPCOM, PlatformAPI (e.g.
// screen readers) and MainProcess. PlatformAPI consumer can only be set
// in parent process, and MainProcess consumer can only be set in child
// process. We only care about PlatformAPI consumer changes because when
// set, we can no longer disable accessibility service.
const { PlatformAPI } = JSON.parse(data);
events.emit(this, "can-be-disabled-change", !PlatformAPI);
} else if (!this.disabling && topic === "nsPref:changed" &&
data === PREF_ACCESSIBILITY_FORCE_DISABLED) {
// PREF_ACCESSIBILITY_FORCE_DISABLED preference change event. When set to
// >=1, it means that the user wants to disable accessibility service and
// prevent it from starting in the future. Note: we also check
// this.disabling state when handling this pref change because this is how
// we disable the accessibility inspector itself.
events.emit(this, "can-be-enabled-change", this.canBeEnabled);
}
},
/**
* Get or create AccessibilityWalker actor, similar to WalkerActor.
*
* @return {Object}
* AccessibleWalkerActor for the current tab.
*/
getWalker() {
if (!this.walker) {
this.walker = new AccessibleWalkerActor(this.conn, this.targetActor);
}
return this.walker;
},
/**
* Destroy accessibility service actor. This method also shutsdown
* accessibility service if possible.
*/
async destroy() {
if (this.destroyed) {
await this.destroyed;
return;
}
let resolver;
this.destroyed = new Promise(resolve => {
resolver = resolve;
});
if (this.walker) {
this.walker.reset();
}
Services.obs.removeObserver(this, "a11y-init-or-shutdown");
if (DebuggerServer.isInChildProcess) {
this.messageManager.removeMessageListener(`${this._msgName}:event`,
this.onMessage);
} else {
Services.obs.removeObserver(this, "a11y-consumers-changed");
Services.prefs.removeObserver(PREF_ACCESSIBILITY_FORCE_DISABLED, this);
}
Actor.prototype.destroy.call(this);
this.walker = null;
this.targetActor = null;
resolver();
},
});
exports.AccessibleActor = AccessibleActor;
exports.AccessibleWalkerActor = AccessibleWalkerActor;
exports.AccessibilityActor = AccessibilityActor;

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

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
'accessibility',
'addon',
'canvas',
'emulation',
@ -20,8 +21,6 @@ DIRS += [
]
DevToolsModules(
'accessibility-parent.js',
'accessibility.js',
'actor-registry.js',
'animation-type-longhand.js',
'animation.js',
@ -63,7 +62,6 @@ DevToolsModules(
'styles.js',
'stylesheets.js',
'thread.js',
'timeline.js',
'webaudio.js',
'webbrowser.js',
'webconsole.js',

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

@ -1,112 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* Many Gecko operations (painting, reflows, restyle, ...) can be tracked
* in real time. A marker is a representation of one operation. A marker
* has a name, start and end timestamps. Markers are stored in docShells.
*
* This actor exposes this tracking mechanism to the devtools protocol.
* Most of the logic is handled in devtools/server/performance/timeline.js
* This just wraps that module up and exposes it via RDP.
*
* For more documentation:
* @see devtools/server/performance/timeline.js
*/
const protocol = require("devtools/shared/protocol");
const { Option, RetVal } = protocol;
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { Timeline } = require("devtools/server/performance/timeline");
const { timelineSpec } = require("devtools/shared/specs/timeline");
/**
* The timeline actor pops and forwards timeline markers registered in docshells.
*/
exports.TimelineActor = protocol.ActorClassWithSpec(timelineSpec, {
/**
* Initializes this actor with the provided connection and target actor.
*/
initialize: function(conn, targetActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.targetActor = targetActor;
this.bridge = new Timeline(targetActor);
this._onTimelineDocLoading = this._onTimelineDocLoading.bind(this);
this._onTimelineMarkers = this._onTimelineMarkers.bind(this);
this._onTimelineTicks = this._onTimelineTicks.bind(this);
this._onTimelineMemory = this._onTimelineMemory.bind(this);
this._onTimelineFrames = this._onTimelineFrames.bind(this);
this.bridge.on("doc-loading", this._onTimelineDocLoading);
this.bridge.on("markers", this._onTimelineMarkers);
this.bridge.on("ticks", this._onTimelineTicks);
this.bridge.on("memory", this._onTimelineMemory);
this.bridge.on("frames", this._onTimelineFrames);
},
/**
* Destroys this actor, stopping recording first.
*/
destroy: function() {
this.bridge.off("doc-loading", this._onTimelineDocLoading);
this.bridge.off("markers", this._onTimelineMarkers);
this.bridge.off("ticks", this._onTimelineTicks);
this.bridge.off("memory", this._onTimelineMemory);
this.bridge.off("frames", this._onTimelineFrames);
this.bridge.destroy();
this.bridge = null;
this.targetActor = null;
protocol.Actor.prototype.destroy.call(this);
},
/**
* Propagate events from the Timeline module over RDP if the event is defined here.
*/
_onTimelineDocLoading: function(...args) {
this.emit("doc-loading", ...args);
},
_onTimelineMarkers: function(...args) {
this.emit("markers", ...args);
},
_onTimelineTicks: function(...args) {
this.emit("ticks", ...args);
},
_onTimelineMemory: function(...args) {
this.emit("memory", ...args);
},
_onTimelineFrames: function(...args) {
this.emit("frames", ...args);
},
isRecording: actorBridgeWithSpec("isRecording", {
request: {},
response: {
value: RetVal("boolean"),
},
}),
start: actorBridgeWithSpec("start", {
request: {
withMarkers: Option(0, "boolean"),
withTicks: Option(0, "boolean"),
withMemory: Option(0, "boolean"),
withFrames: Option(0, "boolean"),
withGCEvents: Option(0, "boolean"),
withDocLoadingEvents: Option(0, "boolean"),
},
response: {
value: RetVal("number"),
},
}),
stop: actorBridgeWithSpec("stop", {
response: {
// Set as possibly nullable due to the end time possibly being
// undefined during destruction
value: RetVal("nullable:number"),
},
}),
});

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

@ -4,9 +4,11 @@
"use strict";
loader.lazyRequireGetter(this, "Ci", "chrome", true);
loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
loader.lazyRequireGetter(this, "CssLogic", "devtools/server/actors/inspector/css-logic", true);
loader.lazyRequireGetter(this, "InspectorActorUtils", "devtools/server/actors/inspector/utils");
loader.lazyRequireGetter(this, "Services");
/**
* Calculates the contrast ratio of the referenced DOM node.
@ -62,4 +64,35 @@ function getContrastRatioFor(node) {
};
}
/**
* Helper function that determines if nsIAccessible object is in defunct state.
*
* @param {nsIAccessible} accessible
* object to be tested.
* @return {Boolean}
* True if accessible object is defunct, false otherwise.
*/
function isDefunct(accessible) {
// If accessibility is disabled, safely assume that the accessible object is
// now dead.
if (!Services.appinfo.accessibilityEnabled) {
return true;
}
let defunct = false;
try {
const extraState = {};
accessible.getState({}, extraState);
// extraState.value is a bitmask. We are applying bitwise AND to mask out
// irrelevant states.
defunct = !!(extraState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
} catch (e) {
defunct = true;
}
return defunct;
}
exports.getContrastRatioFor = getContrastRatioFor;
exports.isDefunct = isDefunct;

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

@ -213,11 +213,6 @@ const ActorRegistry = {
constructor: "CSSUsageActor",
type: { target: true },
});
this.registerModule("devtools/server/actors/timeline", {
prefix: "timeline",
constructor: "TimelineActor",
type: { target: true },
});
if ("nsIProfiler" in Ci &&
!Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
this.registerModule("devtools/server/actors/performance", {
@ -246,7 +241,7 @@ const ActorRegistry = {
constructor: "WebExtensionInspectedWindowActor",
type: { target: true },
});
this.registerModule("devtools/server/actors/accessibility", {
this.registerModule("devtools/server/actors/accessibility/accessibility", {
prefix: "accessibility",
constructor: "AccessibilityActor",
type: { target: true },

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

@ -25,8 +25,6 @@ support-files =
storage-secured-iframe.html
stylesheets-nested-iframes.html
test-spawn-actor-in-parent.js
timeline-iframe-child.html
timeline-iframe-parent.html
storage-helpers.js
!/devtools/client/shared/test/shared-head.js
!/devtools/client/shared/test/telemetry-test-helpers.js
@ -70,9 +68,6 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
[browser_layout_getGrids.js]
[browser_layout_simple.js]
[browser_markers-cycle-collection.js]
[browser_markers-docloading-01.js]
[browser_markers-docloading-02.js]
[browser_markers-docloading-03.js]
[browser_markers-gc.js]
[browser_markers-minor-gc.js]
[browser_markers-parse-html.js]
@ -105,8 +100,5 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
skip-if = (verify && debug && (os == 'mac' || os == 'linux'))
[browser_stylesheets_getTextEmpty.js]
[browser_stylesheets_nested-iframes.js]
[browser_timeline.js]
[browser_timeline_actors.js]
[browser_timeline_iframes.js]
[browser_register_actor.js]
[browser_webextension_inspected_window.js]

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

@ -1,39 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
"use strict";
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(async function() {
const browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
const rec = await front.start({ withMarkers: true });
front.once("doc-loading", e => {
ok(false, "Should not be emitting doc-loading events.");
});
ContentTask.spawn(browser, null, function() {
content.location.reload();
});
await waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers");
await front.stop(rec);
ok(true, "Found the required marker names.");
// Wait some more time to make sure the 'doc-loading' events never get fired.
await DevToolsUtils.waitForTime(1000);
await client.close();
gBrowser.removeCurrentTab();
});

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

@ -1,37 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
"use strict";
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(async function() {
const browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
const rec = await front.start({ withMarkers: true, withDocLoadingEvents: true });
await new Promise(resolve => {
front.once("doc-loading", resolve);
ContentTask.spawn(browser, null, function() {
content.location.reload();
});
});
ok(true, "At least one doc-loading event got fired.");
await waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers");
await front.stop(rec);
ok(true, "Found the required marker names.");
await client.close();
gBrowser.removeCurrentTab();
});

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

@ -1,41 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
"use strict";
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(async function() {
const browser = await addTab(MAIN_DOMAIN + "doc_innerHTML.html");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
const rec = await front.start({ withDocLoadingEvents: true });
waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers").then(e => {
ok(false, "Should not be emitting doc-loading markers.");
});
await new Promise(resolve => {
front.once("doc-loading", resolve);
ContentTask.spawn(browser, null, function() {
content.location.reload();
});
});
ok(true, "At least one doc-loading event got fired.");
await front.stop(rec);
// Wait some more time to make sure the 'doc-loading' markers never get fired.
await DevToolsUtils.waitForTime(1000);
await client.close();
gBrowser.removeCurrentTab();
});

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

@ -1,72 +0,0 @@
/* 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";
// Test that the timeline front's start/stop/isRecording methods work in a
// simple use case, and that markers events are sent when operations occur.
// Note that this test isn't concerned with which markers are actually recorded,
// just that markers are recorded at all.
// Trying to check marker types here may lead to intermittents, see bug 1066474.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(async function() {
await addTab("data:text/html;charset=utf-8,mop");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
ok(front, "The TimelineFront was created");
let isActive = await front.isRecording();
ok(!isActive, "The TimelineFront is not initially recording");
info("Flush any pending reflows");
ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
// forceSyncReflow
content.document.body.innerHeight;
});
info("Start recording");
await front.start({ withMarkers: true });
isActive = await front.isRecording();
ok(isActive, "The TimelineFront is now recording");
info("Change some style on the page to cause style/reflow/paint");
let onMarkers = once(front, "markers");
ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
content.document.body.style.padding = "10px";
});
let markers = await onMarkers;
ok(true, "The markers event was fired");
ok(markers.length > 0, "Markers were returned");
info("Flush pending reflows again");
ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
// forceSyncReflow
content.document.body.innerHeight;
});
info("Change some style on the page to cause style/paint");
onMarkers = once(front, "markers");
ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
content.document.body.style.backgroundColor = "red";
});
markers = await onMarkers;
ok(markers.length > 0, "markers were returned");
await front.stop();
isActive = await front.isRecording();
ok(!isActive, "Not recording after stop()");
await client.close();
gBrowser.removeCurrentTab();
});

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

@ -1,69 +0,0 @@
/* 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/ */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
"use strict";
// Test that the timeline can also record data from the memory and framerate
// actors, emitted as events in tadem with the markers.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(async function() {
await addTab("data:text/html;charset=utf-8,mop");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
info("Start timeline marker recording");
await front.start({ withMemory: true, withTicks: true });
let updatedMemory = 0;
let updatedTicks = 0;
front.on("memory", (delta, measurement) => {
ok(delta > 0, "The delta should be a timestamp.");
ok(measurement, "The measurement should not be null.");
ok(measurement.total > 0, "There should be a 'total' value in the measurement.");
info("Received 'memory' event at " + delta + " with " + measurement.toSource());
updatedMemory++;
});
front.on("ticks", (delta, ticks) => {
ok(delta > 0, "The delta should be a timestamp.");
ok(ticks, "The ticks should not be null.");
info("Received 'ticks' event with " + ticks.toSource());
updatedTicks++;
});
ok((await waitUntil(() => updatedMemory > 1)),
"Some memory measurements were emitted.");
ok((await waitUntil(() => updatedTicks > 1)),
"Some refresh driver ticks were emitted.");
info("Stop timeline marker recording");
await front.stop();
await client.close();
gBrowser.removeCurrentTab();
});
/**
* Waits until a predicate returns true.
*
* @param function predicate
* Invoked once in a while until it returns true.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
*/
function waitUntil(predicate, interval = 10) {
if (predicate()) {
return Promise.resolve(true);
}
return new Promise(resolve =>
setTimeout(function() {
waitUntil(predicate).then(() => resolve(true));
}, interval));
}

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

@ -1,42 +0,0 @@
/* 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/ */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
"use strict";
// Test the timeline front receives markers events for operations that occur in
// iframes.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(async function() {
await addTab(MAIN_DOMAIN + "timeline-iframe-parent.html");
initDebuggerServer();
const client = new DebuggerClient(DebuggerServer.connectPipe());
const form = await connectDebuggerClient(client);
const front = TimelineFront(client, form);
info("Start timeline marker recording");
await front.start({ withMarkers: true });
// Check that we get markers for a few iterations of the timer that runs in
// the child frame.
for (let i = 0; i < 3; i++) {
// That's the time the child frame waits before changing styles.
await wait(300);
const markers = await once(front, "markers");
ok(markers.length, "Markers were received for operations in the child frame");
}
info("Stop timeline marker recording");
await front.stop();
await client.close();
gBrowser.removeCurrentTab();
});
function wait(ms) {
return new Promise(resolve =>
setTimeout(resolve, ms));
}

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

@ -1,20 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Timeline iframe test - child frame</title>
</head>
<body>
<h1>Child frame</h1>
<script>
"use strict";
var h1 = document.querySelector("h1");
setInterval(function() {
h1.style.backgroundColor = "rgb(" + ((Math.random() * 255)|0) + "," +
((Math.random() * 255)|0) + "," +
((Math.random() * 255)|0) + ")";
h1.style.width = ((Math.random() * 500)|0) + "px";
}, 300);
</script>
</body>
</html>

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

@ -1,11 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Timeline iframe test - parent frame</title>
</head>
<body>
<h1>Parent frame</h1>
<iframe src="timeline-iframe-child.html"></iframe>
</body>
</html>

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

@ -56,7 +56,7 @@ function runTests() {
function listProcess() {
// Call listProcesses in order to start receiving new process notifications
client.addListener("processListChanged", function listener() {
client.mainRoot.on("processListChanged", function listener() {
client.removeListener("processListChanged", listener);
ok(true, "Received processListChanged event");
getProcess();
@ -77,7 +77,7 @@ function runTests() {
}
function getProcess() {
client.mainRoot.listProcesses(response => {
client.mainRoot.listProcesses().then(response => {
ok(response.processes.length >= 2, "Got at least the parent process and one child");
is(response.processes.length, processCount + 1,
"Got one additional process on the second call to listProcesses");

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

@ -92,12 +92,7 @@ add_task(async function testReloadExitedAddon() {
// The actor id should be the same after the reload
equal(newAddonActor.actor, addonTargetActor.actor);
const onAddonListChanged = new Promise((resolve) => {
client.addListener("addonListChanged", function listener() {
client.removeListener("addonListChanged", listener);
resolve();
});
});
const onAddonListChanged = client.mainRoot.once("addonListChanged");
// Install an upgrade version of the first add-on.
const addonUpgradeFile = getSupportFile("addons/web-extension-upgrade");

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

@ -61,12 +61,12 @@ function run_test() {
const trace = connectPipeTracing();
const client = new DebuggerClient(trace);
let rootClient;
let rootFront;
client.connect().then(([applicationType, traits]) => {
rootClient = RootFront(client);
rootFront = RootFront(client);
rootClient.simpleReturn().then(() => {
rootFront.simpleReturn().then(() => {
ok(false, "Connection was aborted, request shouldn't resolve");
do_test_finished();
}, e => {

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

@ -105,17 +105,17 @@ function run_test() {
const trace = connectPipeTracing();
const client = new DebuggerClient(trace);
let rootClient;
let rootFront;
client.connect().then(([applicationType, traits]) => {
rootClient = RootFront(client);
rootFront = RootFront(client);
const calls = [];
let sequence = 0;
// Execute a call that won't finish processing until 2
// more calls have happened
calls.push(rootClient.promiseReturn(2).then(ret => {
calls.push(rootFront.promiseReturn(2).then(ret => {
// Check right return order
Assert.equal(sequence, 0);
// Check request handling order
@ -124,21 +124,21 @@ function run_test() {
// Put a few requests into the backlog
calls.push(rootClient.simpleReturn().then(ret => {
calls.push(rootFront.simpleReturn().then(ret => {
// Check right return order
Assert.equal(sequence, 1);
// Check request handling order
Assert.equal(ret, sequence++);
}));
calls.push(rootClient.simpleReturn().then(ret => {
calls.push(rootFront.simpleReturn().then(ret => {
// Check right return order
Assert.equal(sequence, 2);
// Check request handling order
Assert.equal(ret, sequence++);
}));
calls.push(rootClient.simpleThrow().then(() => {
calls.push(rootFront.simpleThrow().then(() => {
Assert.ok(false, "simpleThrow shouldn't succeed!");
}, error => {
// Check right return order
@ -150,7 +150,7 @@ function run_test() {
// handlers, meaning that we can't check the actual order with certainty.
const deferAfterRejection = defer();
calls.push(rootClient.promiseThrow().then(() => {
calls.push(rootFront.promiseThrow().then(() => {
Assert.ok(false, "promiseThrow shouldn't succeed!");
}, error => {
// Check right return order
@ -159,7 +159,7 @@ function run_test() {
deferAfterRejection.resolve();
}));
calls.push(rootClient.simpleReturn().then(ret => {
calls.push(rootFront.simpleReturn().then(ret => {
return deferAfterRejection.promise.then(function() {
// Check right return order
Assert.equal(sequence, 5);
@ -170,7 +170,7 @@ function run_test() {
// Break up the backlog with a long request that waits
// for another simpleReturn before completing
calls.push(rootClient.promiseReturn(1).then(ret => {
calls.push(rootFront.promiseReturn(1).then(ret => {
return deferAfterRejection.promise.then(function() {
// Check right return order
Assert.equal(sequence, 6);
@ -179,7 +179,7 @@ function run_test() {
});
}));
calls.push(rootClient.simpleReturn().then(ret => {
calls.push(rootFront.simpleReturn().then(ret => {
return deferAfterRejection.promise.then(function() {
// Check right return order
Assert.equal(sequence, 7);

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

@ -153,6 +153,11 @@ const run_test = Test(async function() {
await client.connect();
// DebuggerClient.connect() will instantiate the regular RootFront
// which will override the one we just registered and mess up with this test.
// So override it again with test one before asserting.
protocol.types.registeredTypes.get("root").actorSpec = rootSpec;
const rootFront = RootFront(conn);
// Trigger some methods that return forms.

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

@ -102,17 +102,17 @@ function run_test() {
DebuggerServer.init();
const trace = connectPipeTracing();
const client = new DebuggerClient(trace);
let rootClient;
let rootFront;
let strfront = null;
const expectRootChildren = function(size) {
Assert.equal(rootActor.__poolMap.size, size + 1);
Assert.equal(rootClient.__poolMap.size, size + 1);
Assert.equal(rootFront.__poolMap.size, size + 1);
};
client.connect().then(([applicationType, traits]) => {
rootClient = RootFront(client);
rootFront = RootFront(client);
// Root actor has no children yet.
expectRootChildren(0);
@ -121,7 +121,7 @@ function run_test() {
"applicationType": "xpcshell-tests",
"traits": []});
Assert.equal(applicationType, "xpcshell-tests");
rootClient.shortString().then(ret => {
rootFront.shortString().then(ret => {
trace.expectSend({"type": "shortString", "to": "<actorid>"});
trace.expectReceive({"value": "abc", "from": "<actorid>"});
@ -133,7 +133,7 @@ function run_test() {
}).then(ret => {
Assert.equal(ret, SHORT_STR);
}).then(() => {
return rootClient.longString();
return rootFront.longString();
}).then(ret => {
trace.expectSend({"type": "longString", "to": "<actorid>"});
trace.expectReceive({"value": {"type": "longString",
@ -166,7 +166,7 @@ function run_test() {
expectRootChildren(0);
}).then(() => {
const deferred = defer();
rootClient.once("string-event", (str) => {
rootFront.once("string-event", (str) => {
trace.expectSend({"type": "emitShortString", "to": "<actorid>"});
trace.expectReceive({"type": "string-event", "str": "abc", "from": "<actorid>"});
@ -179,7 +179,7 @@ function run_test() {
deferred.resolve(value);
});
});
rootClient.emitShortString();
rootFront.emitShortString();
return deferred.promise;
}).then(value => {
Assert.equal(value, SHORT_STR);
@ -188,7 +188,7 @@ function run_test() {
return strfront.release();
}).then(() => {
const deferred = defer();
rootClient.once("string-event", (str) => {
rootFront.once("string-event", (str) => {
trace.expectSend({"type": "emitLongString", "to": "<actorid>"});
trace.expectReceive({"type": "string-event",
"str": {"type": "longString",
@ -221,7 +221,7 @@ function run_test() {
deferred.resolve(value);
});
});
rootClient.emitLongString();
rootFront.emitLongString();
return deferred.promise;
}).then(value => {
Assert.equal(value, LONG_STR);

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

@ -184,7 +184,7 @@ function run_test() {
/Unknown collection type:/, "Should throw for unknown collection type");
const trace = connectPipeTracing();
const client = new DebuggerClient(trace);
let rootClient;
let rootFront;
client.connect().then(([applicationType, traits]) => {
trace.expectReceive({"from": "<actorid>",
@ -192,24 +192,24 @@ function run_test() {
"traits": []});
Assert.equal(applicationType, "xpcshell-tests");
rootClient = RootFront(client);
rootFront = RootFront(client);
rootClient.simpleReturn().then(ret => {
rootFront.simpleReturn().then(ret => {
trace.expectSend({"type": "simpleReturn", "to": "<actorid>"});
trace.expectReceive({"value": 1, "from": "<actorid>"});
Assert.equal(ret, 1);
}).then(() => {
return rootClient.promiseReturn();
return rootFront.promiseReturn();
}).then(ret => {
trace.expectSend({"type": "promiseReturn", "to": "<actorid>"});
trace.expectReceive({"value": 1, "from": "<actorid>"});
Assert.equal(ret, 1);
}).then(() => {
Assert.throws(() => rootClient.simpleArgs(5),
Assert.throws(() => rootFront.simpleArgs(5),
/undefined passed where a value is required/,
"Should throw if simpleArgs is missing an argument.");
return rootClient.simpleArgs(5, 10);
return rootFront.simpleArgs(5, 10);
}).then(ret => {
trace.expectSend({"type": "simpleArgs",
"firstArg": 5,
@ -221,7 +221,7 @@ function run_test() {
Assert.equal(ret.firstResponse, 6);
Assert.equal(ret.secondResponse, 11);
}).then(() => {
return rootClient.optionArgs({
return rootFront.optionArgs({
"option1": 5,
"option2": 10,
});
@ -234,7 +234,7 @@ function run_test() {
Assert.equal(ret.option1, 5);
Assert.equal(ret.option2, 10);
}).then(() => {
return rootClient.optionArgs({});
return rootFront.optionArgs({});
}).then(ret => {
trace.expectSend({"type": "optionArgs", "to": "<actorid>"});
trace.expectReceive({"from": "<actorid>"});
@ -242,33 +242,33 @@ function run_test() {
Assert.ok(typeof (ret.option2) === "undefined");
}).then(() => {
// Explicitly call an optional argument...
return rootClient.optionalArgs(5, 10);
return rootFront.optionalArgs(5, 10);
}).then(ret => {
trace.expectSend({"type": "optionalArgs", "a": 5, "b": 10, "to": "<actorid>"});
trace.expectReceive({"value": 10, "from": "<actorid>"});
Assert.equal(ret, 10);
}).then(() => {
// Now don't pass the optional argument, expect the default.
return rootClient.optionalArgs(5);
return rootFront.optionalArgs(5);
}).then(ret => {
trace.expectSend({"type": "optionalArgs", "a": 5, "to": "<actorid>"});
trace.expectReceive({"value": 200, "from": "<actorid>"});
Assert.equal(ret, 200);
}).then(ret => {
return rootClient.arrayArgs([0, 1, 2, 3, 4, 5]);
return rootFront.arrayArgs([0, 1, 2, 3, 4, 5]);
}).then(ret => {
trace.expectSend({"type": "arrayArgs", "a": [0, 1, 2, 3, 4, 5], "to": "<actorid>"});
trace.expectReceive({"arrayReturn": [0, 1, 2, 3, 4, 5], "from": "<actorid>"});
Assert.equal(ret[0], 0);
Assert.equal(ret[5], 5);
}).then(() => {
return rootClient.arrayArgs([[5]]);
return rootFront.arrayArgs([[5]]);
}).then(ret => {
trace.expectSend({"type": "arrayArgs", "a": [[5]], "to": "<actorid>"});
trace.expectReceive({"arrayReturn": [[5]], "from": "<actorid>"});
Assert.equal(ret[0][0], 5);
}).then(() => {
return rootClient.renamedEcho("hello");
return rootFront.renamedEcho("hello");
}).then(str => {
trace.expectSend({"type": "echo", "a": "hello", "to": "<actorid>"});
trace.expectReceive({"value": "hello", "from": "<actorid>"});
@ -276,18 +276,18 @@ function run_test() {
Assert.equal(str, "hello");
const deferred = defer();
rootClient.on("oneway", (response) => {
rootFront.on("oneway", (response) => {
trace.expectSend({"type": "testOneWay", "a": "hello", "to": "<actorid>"});
trace.expectReceive({"type": "oneway", "a": "hello", "from": "<actorid>"});
Assert.equal(response, "hello");
deferred.resolve();
});
Assert.ok(typeof (rootClient.testOneWay("hello")) === "undefined");
Assert.ok(typeof (rootFront.testOneWay("hello")) === "undefined");
return deferred.promise;
}).then(() => {
const deferred = defer();
rootClient.on("falsyOptions", res => {
rootFront.on("falsyOptions", res => {
trace.expectSend({"type": "emitFalsyOptions", "to": "<actorid>"});
trace.expectReceive({"type": "falsyOptions",
"farce": false,
@ -298,7 +298,7 @@ function run_test() {
Assert.ok(res.farce === false);
deferred.resolve();
});
rootClient.emitFalsyOptions();
rootFront.emitFalsyOptions();
return deferred.promise;
}).then(() => {
client.close().then(() => {

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

@ -67,12 +67,12 @@ function run_test() {
const trace = connectPipeTracing();
const client = new DebuggerClient(trace);
let rootClient;
let rootFront;
client.connect().then(function onConnect() {
rootClient = RootFront(client);
rootFront = RootFront(client);
rootClient.simpleReturn().then(() => {
rootFront.simpleReturn().then(() => {
let stack = Components.stack;
while (stack) {
info(stack.name);

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

@ -23,8 +23,8 @@ loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "WebConsoleClient", "devtools/shared/webconsole/client", true);
loader.lazyRequireGetter(this, "AddonClient", "devtools/shared/client/addon-client");
loader.lazyRequireGetter(this, "RootClient", "devtools/shared/client/root-client");
loader.lazyRequireGetter(this, "BrowsingContextFront", "devtools/shared/fronts/targets/browsing-context", true);
loader.lazyRequireGetter(this, "RootFront", "devtools/shared/fronts/root", true);
loader.lazyRequireGetter(this, "BrowsingContextTargetFront", "devtools/shared/fronts/targets/browsing-context", true);
loader.lazyRequireGetter(this, "WorkerTargetFront", "devtools/shared/fronts/targets/worker", true);
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
@ -80,7 +80,8 @@ function DebuggerClient(transport) {
*/
this.mainRoot = null;
this.expectReply("root", (packet) => {
this.mainRoot = new RootClient(this, packet);
this.mainRoot = new RootFront(this, packet);
this._frontPool.manage(this.mainRoot);
this.emit("connected", packet.applicationType, packet.traits);
});
}
@ -372,7 +373,7 @@ DebuggerClient.prototype = {
attachTarget: async function(targetActor) {
let front = this._frontPool.actor(targetActor);
if (!front) {
front = new BrowsingContextFront(this, { actor: targetActor });
front = new BrowsingContextTargetFront(this, { actor: targetActor });
this._frontPool.manage(front);
}
@ -799,6 +800,16 @@ DebuggerClient.prototype = {
return;
}
// Check for "forwardingCancelled" here instead of using a front to handle it.
// This is necessary because we might receive this event while the client is closing,
// and the fronts have already been removed by that point.
if (this.mainRoot &&
packet.from == this.mainRoot.actorID &&
packet.type == "forwardingCancelled") {
this.purgeRequests(packet.prefix);
return;
}
// If we have a registered Front for this actor, let it handle the packet
// and skip all the rest of this unpleasantness.
const front = this.getActor(packet.from);
@ -807,16 +818,6 @@ DebuggerClient.prototype = {
return;
}
// Check for "forwardingCancelled" here instead of using a client to handle it.
// This is necessary because we might receive this event while the client is closing,
// and the clients have already been removed by that point.
if (this.mainRoot &&
packet.from == this.mainRoot.actor &&
packet.type == "forwardingCancelled") {
this.purgeRequests(packet.prefix);
return;
}
if (this._clients.has(packet.from) && packet.type) {
const client = this._clients.get(packet.from);
const type = packet.type;

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

@ -16,7 +16,6 @@ DevToolsModules(
'long-string-client.js',
'object-client.js',
'property-iterator-client.js',
'root-client.js',
'source-client.js',
'symbol-iterator-client.js',
'thread-client.js',

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

@ -25,7 +25,7 @@ const noop = () => {};
* is a front to the thread actor created in the server side, hiding the
* protocol details in a traditional JavaScript API.
*
* @param client DebuggerClient, WorkerTargetFront or BrowsingContextFront
* @param client DebuggerClient, WorkerTargetFront or BrowsingContextTargetFront
* The parent of the thread (tab for target-scoped debuggers,
* DebuggerClient for chrome debuggers).
* @param actor string

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

@ -32,12 +32,12 @@ DevToolsModules(
'preference.js',
'promises.js',
'reflow.js',
'root.js',
'screenshot.js',
'storage.js',
'string.js',
'styles.js',
'stylesheets.js',
'timeline.js',
'webaudio.js',
'webgl.js'
)

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

@ -1,119 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci } = require("chrome");
const { arg, DebuggerClient } = require("devtools/shared/client/debugger-client");
const {Ci} = require("chrome");
const {rootSpec} = require("devtools/shared/specs/root");
const protocol = require("devtools/shared/protocol");
const {custom} = protocol;
loader.lazyRequireGetter(this, "getFront", "devtools/shared/protocol", true);
/**
* A RootClient object represents a root actor on the server. Each
* DebuggerClient keeps a RootClient instance representing the root actor
* for the initial connection; DebuggerClient's 'listTabs' and
* 'listChildProcesses' methods forward to that root actor.
*
* @param client object
* The client connection to which this actor belongs.
* @param greeting string
* The greeting packet from the root actor we're to represent.
*
* Properties of a RootClient instance:
*
* @property actor string
* The name of this child's root actor.
* @property applicationType string
* The application type, as given in the root actor's greeting packet.
* @property traits object
* The traits object, as given in the root actor's greeting packet.
*/
function RootClient(client, greeting) {
this._client = client;
this.actor = greeting.from;
this.applicationType = greeting.applicationType;
this.traits = greeting.traits;
const RootFront = protocol.FrontClassWithSpec(rootSpec, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client, { actor: form.from });
// Cache root form as this will always be the same value.
Object.defineProperty(this, "rootForm", {
get() {
delete this.rootForm;
this.rootForm = this._getRoot();
return this.rootForm;
},
configurable: true,
});
this.applicationType = form.applicationType;
this.traits = form.traits;
// Cache of already created global scoped fronts
// [typeName:string => Front instance]
this.fronts = new Map();
}
exports.RootClient = RootClient;
// Cache root form as this will always be the same value.
Object.defineProperty(this, "rootForm", {
get() {
delete this.rootForm;
this.rootForm = this.getRoot();
return this.rootForm;
},
configurable: true,
});
RootClient.prototype = {
constructor: RootClient,
// Cache of already created global scoped fronts
// [typeName:string => Front instance]
this.fronts = new Map();
/**
* Gets the "root" form, which lists all the global actors that affect the entire
* browser. This can replace usages of `listTabs` that only wanted the global actors
* and didn't actually care about tabs.
*/
_getRoot: DebuggerClient.requester({ type: "getRoot" }),
/**
* List the open tabs.
*
* @param object options
* Optional flags for listTabs:
* - boolean favicons: return favicon data
* @param function onResponse
* Called with the response packet.
*/
listTabs: DebuggerClient.requester({ type: "listTabs", options: arg(0) }),
/**
* List the installed addons.
*
* @param function onResponse
* Called with the response packet.
*/
listAddons: DebuggerClient.requester({ type: "listAddons" }),
/**
* List the registered workers.
*
* @param function onResponse
* Called with the response packet.
*/
listWorkers: DebuggerClient.requester({ type: "listWorkers" }),
/**
* List the registered service workers.
*
* @param function onResponse
* Called with the response packet.
*/
listServiceWorkerRegistrations: DebuggerClient.requester({
type: "listServiceWorkerRegistrations",
}),
/**
* List the running processes.
*
* @param function onResponse
* Called with the response packet.
*/
listProcesses: DebuggerClient.requester({ type: "listProcesses" }),
/**
* Fetch the ParentProcessTargetActor for the main process or ContentProcessTargetActor
* for a a given child process ID.
*
* @param number id
* The ID for the process to attach (returned by `listProcesses`).
* Connected to the main process if is 0.
*/
getProcess: DebuggerClient.requester({ type: "getProcess", id: arg(0) }),
this._client = client;
},
/**
* Retrieve all service worker registrations as well as workers from the parent and
@ -229,12 +148,8 @@ RootClient.prototype = {
* If nothing is specified, returns the actor for the currently
* selected tab.
*/
getTab: function(filter) {
const packet = {
to: this.actor,
type: "getTab",
};
getTab: custom(async function(filter) {
const packet = {};
if (filter) {
if (typeof (filter.outerWindowID) == "number") {
packet.outerWindowID = filter.outerWindowID;
@ -260,27 +175,20 @@ RootClient.prototype = {
}
}
return this.request(packet);
},
return this._getTab(packet);
}, {
impl: "_getTab",
}),
/**
* Fetch the ChromeWindowTargetActor for a specific window, like a browser window in
* Firefox, but it can be used to reach any window in the process.
* Test request that returns the object passed as first argument.
*
* @param number outerWindowID
* The outerWindowID of the top level window you are looking for.
* `echo` is special as all the property of the given object have to be passed
* on the packet object. That's not something that can be achieve by requester helper.
*/
getWindow: function({ outerWindowID }) {
if (!outerWindowID) {
throw new Error("Must specify outerWindowID");
}
const packet = {
to: this.actor,
type: "getWindow",
outerWindowID,
};
echo(packet) {
packet.type = "echo";
return this.request(packet);
},
@ -301,45 +209,5 @@ RootClient.prototype = {
this.fronts.set(typeName, front);
return front;
},
/**
* Description of protocol's actors and methods.
*
* @param function onResponse
* Called with the response packet.
*/
protocolDescription: DebuggerClient.requester({ type: "protocolDescription" }),
/**
* Special request, actually supported by all actors to retrieve the list of all
* the request names supported by this actor.
*/
requestTypes: DebuggerClient.requester({ type: "requestTypes" }),
/**
* Test request that returns the object passed as first argument.
*
* `echo` is special as all the property of the given object have to be passed
* on the packet object. That's not something that can be achieve by requester helper.
*/
echo(object) {
const packet = Object.assign(object, {
to: this.actor,
type: "echo",
});
return this.request(packet);
},
/*
* Methods constructed by DebuggerClient.requester require these forwards
* on their 'this'.
*/
get _transport() {
return this._client._transport;
},
get request() {
return this._client.request;
},
};
module.exports = RootClient;
});
exports.RootFront = RootFront;

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

@ -9,7 +9,8 @@ const {custom} = protocol;
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
const BrowsingContextFront = protocol.FrontClassWithSpec(browsingContextTargetSpec, {
const BrowsingContextTargetFront =
protocol.FrontClassWithSpec(browsingContextTargetSpec, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client, form);
@ -102,4 +103,4 @@ const BrowsingContextFront = protocol.FrontClassWithSpec(browsingContextTargetSp
},
});
exports.BrowsingContextFront = BrowsingContextFront;
exports.BrowsingContextTargetFront = BrowsingContextTargetFront;

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

@ -1,25 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
Front,
FrontClassWithSpec,
} = require("devtools/shared/protocol");
const { timelineSpec } = require("devtools/shared/specs/timeline");
/**
* TimelineFront, the front for the TimelineActor.
*/
const TimelineFront = FrontClassWithSpec(timelineSpec, {
initialize: function(client, { timelineActor }) {
Front.prototype.initialize.call(this, client, { actor: timelineActor });
this.manage(this);
},
destroy: function() {
Front.prototype.destroy.call(this);
},
});
exports.TimelineFront = TimelineFront;

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

@ -269,11 +269,6 @@ const Types = exports.__TypesForTests = [
spec: "devtools/shared/specs/targets/worker",
front: null,
},
{
types: ["timeline"],
spec: "devtools/shared/specs/timeline",
front: "devtools/shared/fronts/timeline",
},
{
types: ["audionode", "webaudio"],
spec: "devtools/shared/specs/webaudio",

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

@ -42,6 +42,7 @@ DevToolsModules(
'promises.js',
'property-iterator.js',
'reflow.js',
'root.js',
'screenshot.js',
'script.js',
'source.js',

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

@ -0,0 +1,119 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { types, generateActorSpec, RetVal, Arg, Option } = require("devtools/shared/protocol");
types.addDictType("root.getTab", {
tab: "json",
});
types.addDictType("root.getWindow", {
window: "json",
});
types.addDictType("root.listAddons", {
addons: "array:json",
});
types.addDictType("root.listWorkers", {
workers: "array:json",
});
types.addDictType("root.listServiceWorkerRegistrations", {
registrations: "array:json",
});
types.addDictType("root.listProcesses", {
processes: "array:json",
});
const rootSpecPrototype = {
typeName: "root",
methods: {
getRoot: {
request: {},
response: RetVal("json"),
},
listTabs: {
request: {},
response: RetVal("json"),
},
getTab: {
request: {
outerWindowID: Option(0, "number"),
tabId: Option(0, "number"),
},
response: RetVal("root.getTab"),
},
getWindow: {
request: {
outerWindowID: Option(0, "number"),
},
response: RetVal("root.getWindow"),
},
listAddons: {
request: {},
response: RetVal("root.listAddons"),
},
listWorkers: {
request: {},
response: RetVal("root.listWorkers"),
},
listServiceWorkerRegistrations: {
request: {},
response: RetVal("root.listServiceWorkerRegistrations"),
},
listProcesses: {
request: {},
response: RetVal("root.listProcesses"),
},
getProcess: {
request: {
id: Arg(0, "number"),
},
response: RetVal("json"),
},
protocolDescription: {
request: {},
response: RetVal("json"),
},
requestTypes: {
request: {},
response: RetVal("json"),
},
// Note that RootFront also implements 'echo' requests
// that can't be described via protocol.js specs.
},
events: {
tabListChanged: {
type: "tabListChanged",
},
workerListChanged: {
type: "workerListChanged",
},
addonListChanged: {
type: "addonListChanged",
},
serviceWorkerRegistrationListChanged: {
type: "serviceWorkerRegistrationListChanged",
},
processListChanged: {
type: "processListChanged",
},
},
};
const rootSpec = generateActorSpec(rootSpecPrototype);
exports.rootSpecPrototype = rootSpecPrototype;
exports.rootSpec = rootSpec;

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

@ -12845,7 +12845,6 @@ nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection)
{
*aUseTrackingProtection = false;
bool cbEnabled = StaticPrefs::browser_contentblocking_enabled();
static bool sTPEnabled = false;
static bool sTPInPBEnabled = false;
static bool sPrefsInit = false;
@ -12858,8 +12857,8 @@ nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection)
"privacy.trackingprotection.pbmode.enabled", false);
}
if (mUseTrackingProtection || (cbEnabled && sTPEnabled) ||
(cbEnabled && UsePrivateBrowsing() && sTPInPBEnabled)) {
if (mUseTrackingProtection || sTPEnabled ||
(UsePrivateBrowsing() && sTPInPBEnabled)) {
*aUseTrackingProtection = true;
return NS_OK;
}

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

@ -55,6 +55,8 @@ support-files =
head.js
frame-head.js
file_click_link_within_view_source.html
onload_message.html
onpageshow_message.html
[browser_bug1206879.js]
[browser_bug1309900_crossProcessHistoryNavigation.js]

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

@ -122,3 +122,67 @@ add_task(async function() {
todo_isnot(browsingContext2.parent, browsingContext1);
});
});
add_task(async function() {
await BrowserTestUtils.withNewTab({ gBrowser, url: getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com") + "dummy_page.html" },
async function(browser) {
let path = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
await ContentTask.spawn(browser, path, async function(path) {
function waitForMessage(command) {
let r;
let p = new Promise(resolve => {
content.window.addEventListener('message', e => resolve({result: r, event: e}),
{once: true});
});
r = command();
return p;
}
// Open a new window and wait for the message.
let { result: win, event: e1 } =
await waitForMessage(
_ => content.window.open(path + "onpageshow_message.html"));
is(e1.data, "pageshow");
{
// Create, attach and load an iframe into the window's document.
let frame = win.document.createElement('iframe');
win.document.body.appendChild(frame);
frame.src = "dummy_page.html";
await ContentTaskUtils.waitForEvent(frame, "load");
}
is(win.frames.length, 1, "Here we should have an iframe");
// The frame should have expected browsing context and docshell.
let frameBC = win.frames[0].docShell.browsingContext;
let winDocShell = win.frames[0].docShell;
// Navigate the window and wait for the message.
let { event: e2 } = await waitForMessage(
_ => win.location = path + "onload_message.html");
is(e2.data, "load");
is(win.frames.length, 0, "Here there shouldn't be an iframe");
// Return to the previous document. N.B. we expect to trigger
// BFCache here, hence we wait for pageshow.
let { event: e3 } = await waitForMessage(_ => win.history.back());
is(e3.data, "pageshow");
is(win.frames.length, 1, "And again there should be an iframe");
is(winDocShell, win.frames[0].docShell, "BF cache cached docshell");
is(frameBC, win.frames[0].docShell.browsingContext, "BF cache cached BC");
is(frameBC.id, win.frames[0].docShell.browsingContext.id,
"BF cached BC's have same id");
is(win.docShell.browsingContext.getChildren()[0], frameBC,
"BF cached BC's should still be a child of its parent");
is(win.docShell.browsingContext, frameBC.parent,
"BF cached BC's should still be a connected to its parent");
win.close();
});
});
});

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

@ -0,0 +1,13 @@
<html>
<head>
<meta charset="utf-8">
<script>
addEventListener('load', function() {
opener.postMessage("load", "*");
});
</script>
</head>
<body>
This file posts a message containing "load" to opener on load completion.
</body>
</html>

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

@ -0,0 +1,13 @@
<html>
<head>
<meta charset="utf-8">
<script>
addEventListener('pageshow', function() {
opener.postMessage("pageshow", "*");
});
</script>
</head>
<body>
This file posts a message containing "pageshow" to opener on pageshow.
</body>
</html>

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

@ -211,11 +211,9 @@ ContentPermissionRequestParent::IsBeingDestroyed()
NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
ContentPermissionType::ContentPermissionType(const nsACString& aType,
const nsACString& aAccess,
const nsTArray<nsString>& aOptions)
{
mType = aType;
mAccess = aAccess;
mOptions = aOptions;
}
@ -230,13 +228,6 @@ ContentPermissionType::GetType(nsACString& aType)
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionType::GetAccess(nsACString& aAccess)
{
aAccess = mAccess;
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionType::GetOptions(nsIArray** aOptions)
{
@ -276,7 +267,6 @@ nsContentPermissionUtils::ConvertPermissionRequestToArray(nsTArray<PermissionReq
for (uint32_t i = 0; i < len; i++) {
RefPtr<ContentPermissionType> cpt =
new ContentPermissionType(aSrcArray[i].type(),
aSrcArray[i].access(),
aSrcArray[i].options());
aDesArray->AppendElement(cpt);
}
@ -292,9 +282,7 @@ nsContentPermissionUtils::ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
nsAutoCString type;
nsAutoCString access;
cpt->GetType(type);
cpt->GetAccess(access);
nsCOMPtr<nsIArray> optionArray;
cpt->GetOptions(getter_AddRefs(optionArray));
@ -312,7 +300,7 @@ nsContentPermissionUtils::ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
}
}
aDesArray.AppendElement(PermissionRequest(type, access, options));
aDesArray.AppendElement(PermissionRequest(type, options));
}
return len;
}
@ -335,14 +323,12 @@ ContentPermissionRequestChildMap()
/* static */ nsresult
nsContentPermissionUtils::CreatePermissionArray(const nsACString& aType,
const nsACString& aAccess,
const nsTArray<nsString>& aOptions,
nsIArray** aTypesArray)
{
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
RefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
aAccess,
aOptions);
aOptions);
types->AppendElement(permType);
types.forget(aTypesArray);

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

@ -48,14 +48,12 @@ public:
NS_DECL_NSICONTENTPERMISSIONTYPE
ContentPermissionType(const nsACString& aType,
const nsACString& aAccess,
const nsTArray<nsString>& aOptions);
protected:
virtual ~ContentPermissionType();
nsCString mType;
nsCString mAccess;
nsTArray<nsString> mOptions;
};
@ -72,7 +70,6 @@ public:
static nsresult
CreatePermissionArray(const nsACString& aType,
const nsACString& aAccess,
const nsTArray<nsString>& aOptions,
nsIArray** aTypesArray);

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

@ -1652,8 +1652,7 @@ nsDocument::~nsDocument()
// fastblock is enabled and we're not a private document. We always report
// the all probe, and for the rest, report each category's probe depending
// on whether the respective bit has been set in our enum set.
if (StaticPrefs::browser_contentblocking_enabled() &&
StaticPrefs::browser_fastblock_enabled() &&
if (StaticPrefs::browser_fastblock_enabled() &&
!nsContentUtils::IsInPrivateBrowsing(this)) {
for (auto label : mTrackerBlockedReasons) {
AccumulateCategorical(label);
@ -12957,8 +12956,7 @@ void
nsIDocument::MaybeAllowStorageForOpener()
{
if (StaticPrefs::network_cookie_cookieBehavior() !=
nsICookieService::BEHAVIOR_REJECT_TRACKER ||
!AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions()) {
nsICookieService::BEHAVIOR_REJECT_TRACKER) {
return;
}
@ -13966,8 +13964,7 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
bool granted = true;
bool isTrackingWindow = false;
if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() &&
StaticPrefs::network_cookie_cookieBehavior() ==
if (StaticPrefs::network_cookie_cookieBehavior() ==
nsICookieService::BEHAVIOR_REJECT_TRACKER) {
// Only do something special for third-party tracking content.
if (nsContentUtils::StorageDisabledByAntiTracking(this, nullptr)) {

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

@ -2035,8 +2035,7 @@ nsGlobalWindowOuter::SetNewDocument(nsIDocument* aDocument,
mHasStorageAccess = false;
nsIURI* uri = aDocument->GetDocumentURI();
if (newInnerWindow) {
if (AntiTrackingCommon::ShouldHonorContentBlockingCookieRestrictions() &&
StaticPrefs::network_cookie_cookieBehavior() ==
if (StaticPrefs::network_cookie_cookieBehavior() ==
nsICookieService::BEHAVIOR_REJECT_TRACKER &&
nsContentUtils::IsThirdPartyWindowOrChannel(newInnerWindow, nullptr,
uri) &&

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

@ -303,7 +303,6 @@ nsGeolocationRequest::GetTypes(nsIArray** aTypes)
{
nsTArray<nsString> emptyOptions;
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
NS_LITERAL_CSTRING("unused"),
emptyOptions,
aTypes);
}

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

@ -56,7 +56,6 @@ AutoplayPermissionRequest::GetTypes(nsIArray** aTypes)
nsTArray<nsString> emptyOptions;
return dom::nsContentPermissionUtils::CreatePermissionArray(
NS_LITERAL_CSTRING("autoplay-media"),
NS_LITERAL_CSTRING("unused"),
emptyOptions,
aTypes);
}

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

@ -63,7 +63,6 @@
}
SpecialPowers.pushPrefEnv({
"set": [
["browser.contentblocking.enabled", true],
["network.cookie.cookieBehavior", testData[testIndex].cookieBehavior],
],
}, () => {

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

@ -21,12 +21,6 @@ interface nsIContentPermissionType : nsISupports {
*/
readonly attribute ACString type;
/**
* The access of the permission request, such as
* "read".
*/
readonly attribute ACString access;
/**
* The array of available options.
*/

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

@ -7,7 +7,6 @@ namespace dom {
struct PermissionRequest {
nsCString type;
nsCString access;
nsString[] options;
};

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

@ -69,7 +69,6 @@ MIDIPermissionRequest::GetTypes(nsIArray** aTypes)
options.AppendElement(NS_LITERAL_STRING("sysex"));
}
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("midi"),
NS_LITERAL_CSTRING("unused"),
options,
aTypes);
}

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

@ -680,7 +680,6 @@ NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
{
nsTArray<nsString> emptyOptions;
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
NS_LITERAL_CSTRING("unused"),
emptyOptions,
aTypes);
}

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

@ -180,7 +180,6 @@ Push.prototype = {
// Create an array with a single nsIContentPermissionType element.
let type = {
type: "desktop-notification",
access: null,
options: [],
QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
};

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

@ -814,7 +814,6 @@ PersistentStoragePermissionRequest::GetTypes(nsIArray** aTypes)
return nsContentPermissionUtils::CreatePermissionArray(
NS_LITERAL_CSTRING("persistent-storage"),
NS_LITERAL_CSTRING("unused"),
emptyOptions,
aTypes);
}

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

@ -84,7 +84,6 @@ function runTest(aExpectedResponses) {
// the given cookie policy.
function testShouldIntercept(behavior, lifetime, done) {
SpecialPowers.pushPrefEnv({"set": [
["browser.contentblocking.enabled", true],
["network.cookie.cookieBehavior", behavior],
["network.cookie.lifetimePolicy", lifetime],
]}, function() {

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

@ -28,7 +28,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1022869
waitForLoad().then(function() {
// Cookies are set up, disallow third-party cookies and start the test.
SpecialPowers.pushPrefEnv({ set: [
["browser.contentblocking.enabled", true],
["network.cookie.cookieBehavior", 1],
]}, () => { continueTest(); });
}).catch((e) => { ok(false, `Got exception: ${e}`) });

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

@ -27,7 +27,6 @@ if (inFrame) {
function setCookieBehavior(behavior) {
return SpecialPowers.pushPrefEnv({"set": [
["browser.contentblocking.enabled", true],
[kPrefName, behavior],
]});
}

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

@ -16,7 +16,6 @@ SimpleTest.waitForExplicitFinish();
// Set cookies behavior to "always reject".
SpecialPowers.pushPrefEnv({"set": [
["browser.contentblocking.enabled", true],
["network.cookie.cookieBehavior", 2],
]}, test1);

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