Bug 1663647, convert talos tab switching test to use actor-based RemotePageChild and remove usage of old RemotePageManager, r=mconley,perftest-reviewers,sparky

Differential Revision: https://phabricator.services.mozilla.com/D167358
This commit is contained in:
Neil Deakin 2023-01-24 14:47:08 +00:00
Родитель 5de446476e
Коммит 768a4ed3e6
9 изменённых файлов: 482 добавлений и 448 удалений

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

@ -0,0 +1,88 @@
/* 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/. */
const { ComponentUtils } = ChromeUtils.import(
"resource://gre/modules/ComponentUtils.jsm"
);
const { RemotePageChild } = ChromeUtils.import(
"resource://gre/actors/RemotePageChild.jsm"
);
const WEBEXTENSION_ID = "tabswitch-talos@mozilla.org";
const ABOUT_PAGE_NAME = "tabswitch";
const Registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
const UUID = "0f459ab4-b4ba-4741-ac89-ee47dea07adb";
const ABOUT_PATH_PATH = "content/test.html";
const { WebExtensionPolicy } = Cu.getGlobalForObject(Services);
let factory;
export class TalosTabSwitchChild extends RemotePageChild {
actorCreated() {
// Ignore about:blank pages that can get here.
if (!String(this.document.location).startsWith("about:tabswitch")) {
return;
}
// If an error occurs, it was probably already added by an earlier test run.
try {
this.addPage("about:tabswitch", {
RPMSendQuery: ["tabswitch-do-test"],
});
} catch {}
super.actorCreated();
}
handleEvent(event) {}
receiveMessage(message) {
if (message.name == "Tabswitch:Teardown") {
this.teardown();
} else if (message.name == "GarbageCollect") {
this.contentWindow.windowUtils.garbageCollect();
}
}
teardown() {
Registrar.unregisterFactory(Components.ID(UUID), this._factory);
factory = null;
}
}
function setupTabSwitch() {
let extensionPolicy = WebExtensionPolicy.getByID(WEBEXTENSION_ID);
let aboutPageURI = extensionPolicy.getURL(ABOUT_PATH_PATH);
class TabSwitchAboutModule {
constructor() {
this.QueryInterface = ChromeUtils.generateQI(["nsIAboutModule"]);
}
newChannel(aURI, aLoadInfo) {
let uri = Services.io.newURI(aboutPageURI);
let chan = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
chan.originalURI = aURI;
return chan;
}
getURIFlags(aURI) {
return (
Ci.nsIAboutModule.ALLOW_SCRIPT |
Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD
);
}
}
factory = ComponentUtils.generateSingletonFactory(TabSwitchAboutModule);
Registrar.registerFactory(
Components.ID(UUID),
"",
`@mozilla.org/network/protocol/about;1?what=${ABOUT_PAGE_NAME}`,
factory
);
}
setupTabSwitch();

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

@ -0,0 +1,336 @@
/* 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/. */
let TalosParentProfiler;
export class TalosTabSwitchParent extends JSWindowActorParent {
receiveMessage(message) {
if (message.name == "tabswitch-do-test") {
let browser = this.browsingContext.top.embedderElement;
return this.test(browser.ownerGlobal);
}
return undefined;
}
/**
* Returns a Promise that resolves when browser-delayed-startup-finished
* fires for a given window
*
* @param win
* The window that we're waiting for the notification for.
* @returns Promise
*/
waitForDelayedStartup(win) {
return new Promise(resolve => {
const topic = "browser-delayed-startup-finished";
Services.obs.addObserver(function onStartup(subject) {
if (win == subject) {
Services.obs.removeObserver(onStartup, topic);
resolve();
}
}, topic);
});
}
/**
* For some <xul:tabbrowser>, loads a collection of URLs as new tabs
* in that browser.
*
* @param gBrowser (<xul:tabbrowser>)
* The <xul:tabbrowser> in which to load the new tabs.
* @param urls (Array)
* An array of URL strings to be loaded as new tabs.
* @returns Promise
* Resolves once all tabs have finished loading.
*/
loadTabs(gBrowser, urls) {
return new Promise(resolve => {
gBrowser.loadTabs(urls, {
inBackground: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
let waitingToLoad = new Set(urls);
let listener = {
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
"nsISupportsWeakReference",
]),
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
let loadedState =
Ci.nsIWebProgressListener.STATE_STOP |
Ci.nsIWebProgressListener.STATE_IS_NETWORK;
if (
(aStateFlags & loadedState) == loadedState &&
!aWebProgress.isLoadingDocument &&
aWebProgress.isTopLevel &&
Components.isSuccessCode(aStatus)
) {
dump(`Loaded: ${aBrowser.currentURI.spec}\n`);
waitingToLoad.delete(aBrowser.currentURI.spec);
if (!waitingToLoad.size) {
gBrowser.removeTabsProgressListener(listener);
dump("Loads complete - starting tab switches\n");
resolve();
}
}
},
};
gBrowser.addTabsProgressListener(listener);
});
}
/**
* For some <xul:tab> in a browser window, have that window switch
* to that tab. Returns a Promise that resolves ones the tab content
* has been presented to the user.
*/
async switchToTab(tab) {
let browser = tab.linkedBrowser;
let gBrowser = tab.ownerGlobal.gBrowser;
let start = Cu.now();
// We need to wait for the TabSwitchDone event to make sure
// that the async tab switcher has shut itself down.
let switchDone = this.waitForTabSwitchDone(browser);
// Set up our promise that will wait for the content to be
// presented.
let finishPromise = this.waitForContentPresented(browser);
// Finally, do the tab switch.
gBrowser.selectedTab = tab;
await switchDone;
let finish = await finishPromise;
return finish - start;
}
/**
* For some <xul:browser>, find the <xul:tabbrowser> associated with it,
* and wait until that tabbrowser has finished a tab switch. This function
* assumes a tab switch has started, or is about to start.
*
* @param browser (<xul:browser>)
* The browser whose tabbrowser we expect to be involved in a tab
* switch.
* @returns Promise
* Resolves once the TabSwitchDone event is fired.
*/
waitForTabSwitchDone(browser) {
return new Promise(resolve => {
let gBrowser = browser.ownerGlobal.gBrowser;
gBrowser.addEventListener(
"TabSwitchDone",
function onTabSwitchDone() {
resolve();
},
{ once: true }
);
});
}
/**
* For some <xul:browser>, returns a Promise that resolves once its
* content has been presented to the user.
*
* @param browser (<xul:browser>)
* The browser we expect to be presented.
*
* @returns Promise
* Resolves once the content has been presented. Resolves to
* the system time that the presentation occurred at, in
* milliseconds since midnight 01 January, 1970 UTC.
*/
waitForContentPresented(browser) {
return new Promise(resolve => {
browser.addEventListener(
"MozLayerTreeReady",
function onLayersReady(event) {
let now = Cu.now();
TalosParentProfiler.mark("MozLayerTreeReady seen by tabswitch");
resolve(now);
},
{ once: true }
);
});
}
/**
* Do a garbage collect in the parent, and then a garbage
* collection in the content process that the actor is
* running in.
*
* @returns Promise
* Resolves once garbage collection has been completed in the
* parent, and the content process for the actor.
*/
forceGC(win) {
win.windowUtils.garbageCollect();
return this.sendQuery("GarbageCollect");
}
/**
* Given some host window, open a new window, browser its initial tab to
* about:blank, then load up our set of testing URLs. Once they've all finished
* loading, switch through each tab, recording their tab switch times. Finally,
* report the results.
*
* @param window
* A host window. Primarily, we just use this for the OpenBrowserWindow
* function defined in that window.
* @returns Promise
*/
async test(window) {
if (!window.gMultiProcessBrowser) {
dump(
"** The tabswitch Talos test does not support running in non-e10s mode " +
"anymore! Bailing out!\n"
);
return null;
}
TalosParentProfiler = ChromeUtils.importESModule(
"resource://talos-powers/TalosParentProfiler.sys.mjs"
).TalosParentProfiler;
let testURLs = [];
let win = window.OpenBrowserWindow();
try {
let prefFile = Services.prefs.getCharPref("addon.test.tabswitch.urlfile");
if (prefFile) {
testURLs = handleFile(win, prefFile);
}
} catch (ex) {
/* error condition handled below */
}
if (!testURLs || !testURLs.length) {
dump(
"no tabs to test, 'addon.test.tabswitch.urlfile' pref isn't set to page set path\n"
);
return null;
}
await this.waitForDelayedStartup(win);
let gBrowser = win.gBrowser;
// We don't want to catch scrolling the tabstrip in our tests
gBrowser.tabContainer.style.opacity = "0";
let initialTab = gBrowser.selectedTab;
await this.loadTabs(gBrowser, testURLs);
// We'll switch back to about:blank after each tab switch
// in an attempt to put the graphics layer into a "steady"
// state before switching to the next tab.
initialTab.linkedBrowser.loadURI("about:blank", {
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
});
let tabs = gBrowser.getTabsToTheEndFrom(initialTab);
let times = [];
for (let tab of tabs) {
// Let's do an initial run to warm up any paint related caches
// (like glyph caches for text). In the next loop we will start with
// a GC before each switch so we don't need here.
await this.switchToTab(tab);
await this.switchToTab(initialTab);
}
for (let tab of tabs) {
// Moving a tab causes expensive style/layout computations on the tab bar
// that are delayed using requestAnimationFrame, so wait for an animation
// frame callback + one tick to ensure we aren't measuring the time it
// takes to move a tab.
gBrowser.moveTabTo(tab, 1);
await new Promise(resolve => win.requestAnimationFrame(resolve));
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
await this.forceGC(win);
TalosParentProfiler.resume();
let time = await this.switchToTab(tab);
TalosParentProfiler.pause(
"TabSwitch Test: " + tab.linkedBrowser.currentURI.spec
);
dump(`${tab.linkedBrowser.currentURI.spec}: ${time}ms\n`);
times.push(time);
await this.switchToTab(initialTab);
}
let output =
"<!DOCTYPE html>" +
'<html lang="en">' +
"<head><title>Tab Switch Results</title></head>" +
"<body><h1>Tab switch times</h1>" +
"<table>";
let time = 0;
for (let i in times) {
time += times[i];
output +=
"<tr><td>" + testURLs[i] + "</td><td>" + times[i] + "ms</td></tr>";
}
output += "</table></body></html>";
dump("total tab switch time:" + time + "\n");
let resultsTab = win.gBrowser.addTab(
"data:text/html;charset=utf-8," + encodeURIComponent(output),
{
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
}
);
win.gBrowser.selectedTab = resultsTab;
TalosParentProfiler.afterProfileGathered().then(() => {
win.close();
});
return {
times,
urls: testURLs,
};
}
}
// This just has to match up with the make_talos_domain function in talos.py
function makeTalosDomain(host) {
return host + "-talos";
}
function handleFile(win, file) {
let localFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
localFile.initWithPath(file);
let localURI = Services.io.newFileURI(localFile);
let req = new win.XMLHttpRequest();
req.open("get", localURI.spec, false);
req.send(null);
let testURLs = [];
let maxurls = Services.prefs.getIntPref("addon.test.tabswitch.maxurls");
let lines = req.responseText.split('<a href="');
testURLs = [];
if (maxurls && maxurls > 0) {
lines.splice(maxurls, lines.length);
}
lines.forEach(function(a) {
let url = a.split('"')[0];
if (url != "") {
let domain = url.split("/")[0];
if (domain != "") {
testURLs.push(`http://${makeTalosDomain(domain)}/fis/tp5n/${url}`);
}
}
});
return testURLs;
}

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

@ -2,383 +2,54 @@
/* globals ExtensionAPI, Services */
const { RemotePages } = ChromeUtils.import(
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
let TalosParentProfiler;
/**
* Returns a Promise that resolves when browser-delayed-startup-finished
* fires for a given window
*
* @param win
* The window that we're waiting for the notification for.
* @returns Promise
*/
function waitForDelayedStartup(win) {
return new Promise(resolve => {
const topic = "browser-delayed-startup-finished";
Services.obs.addObserver(function onStartup(subject) {
if (win == subject) {
Services.obs.removeObserver(onStartup, topic);
resolve();
}
}, topic);
});
}
/**
* For some <xul:tabbrowser>, loads a collection of URLs as new tabs
* in that browser.
*
* @param gBrowser (<xul:tabbrowser>)
* The <xul:tabbrowser> in which to load the new tabs.
* @param urls (Array)
* An array of URL strings to be loaded as new tabs.
* @returns Promise
* Resolves once all tabs have finished loading.
*/
function loadTabs(gBrowser, urls) {
return new Promise(resolve => {
gBrowser.loadTabs(urls, {
inBackground: true,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
let waitingToLoad = new Set(urls);
let listener = {
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
"nsISupportsWeakReference",
]),
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
let loadedState =
Ci.nsIWebProgressListener.STATE_STOP |
Ci.nsIWebProgressListener.STATE_IS_NETWORK;
if (
(aStateFlags & loadedState) == loadedState &&
!aWebProgress.isLoadingDocument &&
aWebProgress.isTopLevel &&
Components.isSuccessCode(aStatus)
) {
dump(`Loaded: ${aBrowser.currentURI.spec}\n`);
waitingToLoad.delete(aBrowser.currentURI.spec);
if (!waitingToLoad.size) {
gBrowser.removeTabsProgressListener(listener);
dump("Loads complete - starting tab switches\n");
resolve();
}
}
},
};
gBrowser.addTabsProgressListener(listener);
});
}
/**
* For some <xul:tab> in a browser window, have that window switch
* to that tab. Returns a Promise that resolves ones the tab content
* has been presented to the user.
*/
async function switchToTab(tab) {
let browser = tab.linkedBrowser;
let gBrowser = tab.ownerGlobal.gBrowser;
let start = Cu.now();
// We need to wait for the TabSwitchDone event to make sure
// that the async tab switcher has shut itself down.
let switchDone = waitForTabSwitchDone(browser);
// Set up our promise that will wait for the content to be
// presented.
let finishPromise = waitForContentPresented(browser);
// Finally, do the tab switch.
gBrowser.selectedTab = tab;
await switchDone;
let finish = await finishPromise;
return finish - start;
}
/**
* For some <xul:browser>, find the <xul:tabbrowser> associated with it,
* and wait until that tabbrowser has finished a tab switch. This function
* assumes a tab switch has started, or is about to start.
*
* @param browser (<xul:browser>)
* The browser whose tabbrowser we expect to be involved in a tab
* switch.
* @returns Promise
* Resolves once the TabSwitchDone event is fired.
*/
function waitForTabSwitchDone(browser) {
return new Promise(resolve => {
let gBrowser = browser.ownerGlobal.gBrowser;
gBrowser.addEventListener(
"TabSwitchDone",
function onTabSwitchDone() {
resolve();
},
{ once: true }
);
});
}
/**
* For some <xul:browser>, returns a Promise that resolves once its
* content has been presented to the user.
*
* @param browser (<xul:browser>)
* The browser we expect to be presented.
*
* @returns Promise
* Resolves once the content has been presented. Resolves to
* the system time that the presentation occurred at, in
* milliseconds since midnight 01 January, 1970 UTC.
*/
function waitForContentPresented(browser) {
return new Promise(resolve => {
browser.addEventListener(
"MozLayerTreeReady",
function onLayersReady(event) {
let now = Cu.now();
TalosParentProfiler.mark("MozLayerTreeReady seen by tabswitch");
resolve(now);
},
{ once: true }
);
});
}
/**
* Given some browser, do a garbage collect in the parent, and then
* a garbage collection in the content process that the browser is
* running in.
*
* @param browser (<xul:browser>)
* The browser in which to do the garbage collection.
* @returns Promise
* Resolves once garbage collection has been completed in the
* parent, and the content process for the browser (if applicable).
*/
function forceGC(win, browser) {
// TODO: Find a better way of letting Talos force GC in the child. We're
// stealing a chunk of pageloader to do this, and we should probably put
// something into TalosPowers instead.
browser.messageManager.loadFrameScript(
"chrome://pageloader/content/talos-content.js",
false
);
win.windowUtils.garbageCollect();
return new Promise(resolve => {
let mm = browser.messageManager;
mm.addMessageListener("Talos:ForceGC:OK", function onTalosContentForceGC(
msg
) {
mm.removeMessageListener("Talos:ForceGC:OK", onTalosContentForceGC);
resolve();
});
mm.sendAsyncMessage("Talos:ForceGC");
});
}
/**
* Given some host window, open a new window, browser its initial tab to
* about:blank, then load up our set of testing URLs. Once they've all finished
* loading, switch through each tab, recording their tab switch times. Finally,
* report the results.
*
* @param window
* A host window. Primarily, we just use this for the OpenBrowserWindow
* function defined in that window.
* @returns Promise
*/
async function test(window) {
if (!window.gMultiProcessBrowser) {
dump(
"** The tabswitch Talos test does not support running in non-e10s mode " +
"anymore! Bailing out!\n"
);
return;
}
TalosParentProfiler = ChromeUtils.importESModule(
"resource://talos-powers/TalosParentProfiler.sys.mjs"
).TalosParentProfiler;
let testURLs = [];
let win = window.OpenBrowserWindow();
try {
let prefFile = Services.prefs.getCharPref("addon.test.tabswitch.urlfile");
if (prefFile) {
testURLs = handleFile(win, prefFile);
}
} catch (ex) {
/* error condition handled below */
}
if (!testURLs || !testURLs.length) {
dump(
"no tabs to test, 'addon.test.tabswitch.urlfile' pref isn't set to page set path\n"
);
return;
}
await waitForDelayedStartup(win);
let gBrowser = win.gBrowser;
// We don't want to catch scrolling the tabstrip in our tests
gBrowser.tabContainer.style.opacity = "0";
let initialTab = gBrowser.selectedTab;
await loadTabs(gBrowser, testURLs);
// We'll switch back to about:blank after each tab switch
// in an attempt to put the graphics layer into a "steady"
// state before switching to the next tab.
initialTab.linkedBrowser.loadURI("about:blank", {
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
});
let tabs = gBrowser.getTabsToTheEndFrom(initialTab);
let times = [];
for (let tab of tabs) {
// Let's do an initial run to warm up any paint related caches
// (like glyph caches for text). In the next loop we will start with
// a GC before each switch so we don't need here.
// Note: in case of multiple content processes, closing all the tabs
// would close the related content processes, and even if we kept them
// alive it would be unlikely that the same pages end up in the same
// content processes, so we cannot do this at the manifest level.
await switchToTab(tab);
await switchToTab(initialTab);
}
for (let tab of tabs) {
// Moving a tab causes expensive style/layout computations on the tab bar
// that are delayed using requestAnimationFrame, so wait for an animation
// frame callback + one tick to ensure we aren't measuring the time it
// takes to move a tab.
gBrowser.moveTabTo(tab, 1);
await new Promise(resolve => win.requestAnimationFrame(resolve));
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
await forceGC(win, tab.linkedBrowser);
TalosParentProfiler.resume();
let time = await switchToTab(tab);
TalosParentProfiler.pause(
"TabSwitch Test: " + tab.linkedBrowser.currentURI.spec
);
dump(`${tab.linkedBrowser.currentURI.spec}: ${time}ms\n`);
times.push(time);
await switchToTab(initialTab);
}
let output =
"<!DOCTYPE html>" +
'<html lang="en">' +
"<head><title>Tab Switch Results</title></head>" +
"<body><h1>Tab switch times</h1>" +
"<table>";
let time = 0;
for (let i in times) {
time += times[i];
output +=
"<tr><td>" + testURLs[i] + "</td><td>" + times[i] + "ms</td></tr>";
}
output += "</table></body></html>";
dump("total tab switch time:" + time + "\n");
win.gBrowser.addTab(
"data:text/html;charset=utf-8," + encodeURIComponent(output),
{
inBackground: false,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
}
);
remotePage.sendAsyncMessage("tabswitch-test-results", {
times,
urls: testURLs,
});
TalosParentProfiler.afterProfileGathered().then(() => {
win.close();
});
}
// This just has to match up with the make_talos_domain function in talos.py
function makeTalosDomain(host) {
return host + "-talos";
}
function handleFile(win, file) {
let localFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
localFile.initWithPath(file);
let localURI = Services.io.newFileURI(localFile);
let req = new win.XMLHttpRequest();
req.open("get", localURI.spec, false);
req.send(null);
let testURLs = [];
let maxurls = Services.prefs.getIntPref("addon.test.tabswitch.maxurls");
let lines = req.responseText.split('<a href="');
testURLs = [];
if (maxurls && maxurls > 0) {
lines.splice(maxurls, lines.length);
}
lines.forEach(function(a) {
let url = a.split('"')[0];
if (url != "") {
let domain = url.split("/")[0];
if (domain != "") {
testURLs.push(`http://${makeTalosDomain(domain)}/fis/tp5n/${url}`);
}
}
});
return testURLs;
}
var remotePage;
this.tabswitch = class extends ExtensionAPI {
getAPI(context) {
return {
tabswitch: {
setup({ processScriptPath }) {
setup() {
AboutNewTab.newTabURL = "about:blank";
const processScriptURL = context.extension.baseURI.resolve(
processScriptPath
let uri = Services.io.newURI(
"actors/",
null,
context.extension.rootURI
);
let resProto = Services.io
.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
resProto.setSubstitution("talos-tabswitch", uri);
let tabSwitchTalosActors = {
parent: {
esModuleURI:
"resource://talos-tabswitch/TalosTabSwitchParent.sys.mjs",
},
child: {
esModuleURI:
"resource://talos-tabswitch/TalosTabSwitchChild.sys.mjs",
events: {
DOMDocElementInserted: { capture: true },
},
},
};
ChromeUtils.registerWindowActor(
"TalosTabSwitch",
tabSwitchTalosActors
);
Services.ppmm.loadProcessScript(processScriptURL, true);
remotePage = new RemotePages("about:tabswitch");
remotePage.addMessageListener("tabswitch-do-test", function doTest(
msg
) {
test(msg.target.browser.ownerGlobal);
});
return () => {
Services.ppmm.sendAsyncMessage("Tabswitch:Teardown");
remotePage.destroy();
ChromeUtils.unregisterWindowActor(
"TalosTabSwitch",
tabSwitchTalosActors
);
AboutNewTab.resetNewTabURL();
resProto.setSubstitution("talos-tabswitch", null);
};
},
},

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

@ -8,6 +8,4 @@
* function that is used to report results.
*/
let processScriptPath = "content/tabswitch-content-process.js";
browser.tabswitch.setup({ processScriptPath });
browser.tabswitch.setup();

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

@ -1,68 +0,0 @@
/* eslint-env mozilla/process-script */
const { ComponentUtils } = ChromeUtils.import(
"resource://gre/modules/ComponentUtils.jsm"
);
const WEBEXTENSION_ID = "tabswitch-talos@mozilla.org";
const ABOUT_PAGE_NAME = "tabswitch";
const Registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
const UUID = "0f459ab4-b4ba-4741-ac89-ee47dea07adb";
const ABOUT_PATH_PATH = "content/test.html";
const { WebExtensionPolicy } = Cu.getGlobalForObject(Services);
const TPSProcessScript = {
init() {
let extensionPolicy = WebExtensionPolicy.getByID(WEBEXTENSION_ID);
let aboutPageURI = extensionPolicy.getURL(ABOUT_PATH_PATH);
class TabSwitchAboutModule {
constructor() {
this.QueryInterface = ChromeUtils.generateQI(["nsIAboutModule"]);
}
newChannel(aURI, aLoadInfo) {
let uri = Services.io.newURI(aboutPageURI);
let chan = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
chan.originalURI = aURI;
return chan;
}
getURIFlags(aURI) {
return (
Ci.nsIAboutModule.ALLOW_SCRIPT |
Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD
);
}
}
let factory = ComponentUtils.generateSingletonFactory(TabSwitchAboutModule);
this._factory = factory;
Registrar.registerFactory(
Components.ID(UUID),
"",
`@mozilla.org/network/protocol/about;1?what=${ABOUT_PAGE_NAME}`,
factory
);
this._hasSetup = true;
},
teardown() {
if (!this._hasSetup) {
return;
}
Registrar.unregisterFactory(Components.ID(UUID), this._factory);
this._hasSetup = false;
this._factory = null;
},
receiveMessage(msg) {
if (msg.name == "Tabswitch:Teardown") {
this.teardown();
}
},
};
TPSProcessScript.init();

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

@ -1,13 +1,11 @@
<html>
<head>
<script>
/* global RPMAddMessageListener, RPMSendAsyncMessage */
/* global RPMSendQuery */
function do_test(override) {
if (override || document.location.hash.indexOf("#auto") == 0) {
RPMSendAsyncMessage("tabswitch-do-test");
RPMAddMessageListener("tabswitch-test-results", function onMessage(msg) {
let data = msg.data;
tpRecordTime(data.times.join(","), 0, data.urls.join(","));
RPMSendQuery("tabswitch-do-test", {}).then(results => {
tpRecordTime(results.times.join(","), 0, results.urls.join(","));
});
}
}

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

@ -7,17 +7,8 @@
"name": "setup",
"type": "function",
"description": "Prepares the tabswitch test to be run by the Talos framework.",
"parameters": [{
"type": "object",
"name": "setupArgs",
"properties": {
"processScriptPath": {
"type": "string",
"description": "Relative path for the process script to load for the test in the initial tab."
}
}
}]
"parameters": []
}
]
}
]
]

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

@ -140,6 +140,10 @@ class RemotePageChild extends JSWindowActorChild {
return true;
}
addPage(aUrl, aFunctionMap) {
lazy.RemotePageAccessManager.addPage(aUrl, aFunctionMap);
}
// Implementation of functions that are exported into the page.
RPMSendAsyncMessage(aName, aData = null) {

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

@ -340,4 +340,20 @@ export let RemotePageAccessManager = {
// if not, deny access.
return accessMapForURI[aFeature];
},
/**
* This function adds a new page to the access map, but can only
* be used in a test environment.
*/
addPage(aUrl, aFunctionMap) {
if (!Cu.isInAutomation) {
throw new Error("Cannot only modify privileges during testing");
}
if (aUrl in this.accessMap) {
throw new Error("Cannot modify privileges of existing page");
}
this.accessMap[aUrl] = aFunctionMap;
},
};