зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
a2c4dad0d5
|
@ -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);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче