/* 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"; /* globals Services */ const { E10SUtils } = ChromeUtils.importESModule( "resource://gre/modules/E10SUtils.sys.mjs" ); const { Preferences } = ChromeUtils.importESModule( "resource://gre/modules/Preferences.sys.mjs" ); // eslint-disable-next-line mozilla/reject-importGlobalProperties Cu.importGlobalProperties(["PathUtils"]); this.test = class extends ExtensionAPI { onStartup() { ChromeUtils.registerWindowActor("TestSupport", { child: { esModuleURI: "resource://android/assets/web_extensions/test-support/TestSupportChild.sys.mjs", }, allFrames: true, }); ChromeUtils.registerProcessActor("TestSupportProcess", { child: { esModuleURI: "resource://android/assets/web_extensions/test-support/TestSupportProcessChild.sys.mjs", }, }); } onShutdown(isAppShutdown) { if (isAppShutdown) { return; } ChromeUtils.unregisterWindowActor("TestSupport"); ChromeUtils.unregisterProcessActor("TestSupportProcess"); } getAPI(context) { /** * Helper function for getting window or process actors. * * @param tabId - id of the tab; required * @param actorName - a string; the name of the actor * Default: "TestSupport" which is our test framework actor * (you can still pass the second parameter when getting the TestSupport actor, for readability) * * @returns actor */ function getActorForTab(tabId, actorName = "TestSupport") { const tab = context.extension.tabManager.get(tabId); const { browsingContext } = tab.browser; return browsingContext.currentWindowGlobal.getActor(actorName); } return { test: { /* Set prefs and returns set of saved prefs */ async setPrefs(oldPrefs, newPrefs) { // Save old prefs Object.assign( oldPrefs, ...Object.keys(newPrefs) .filter(key => !(key in oldPrefs)) .map(key => ({ [key]: Preferences.get(key, null) })) ); // Set new prefs Preferences.set(newPrefs); return oldPrefs; }, /* Restore prefs to old value. */ async restorePrefs(oldPrefs) { for (const [name, value] of Object.entries(oldPrefs)) { if (value === null) { Preferences.reset(name); } else { Preferences.set(name, value); } } }, /* Get pref values. */ async getPrefs(prefs) { return Preferences.get(prefs); }, /* Gets link color for a given selector. */ async getLinkColor(tabId, selector) { return getActorForTab(tabId, "TestSupport").sendQuery( "GetLinkColor", { selector } ); }, async getRequestedLocales() { return Services.locale.requestedLocales; }, async getPidForTab(tabId) { const tab = context.extension.tabManager.get(tabId); const pids = E10SUtils.getBrowserPids(tab.browser); return pids[0]; }, async waitForContentTransformsReceived(tabId) { return getActorForTab(tabId).sendQuery( "WaitForContentTransformsReceived" ); }, async getAllBrowserPids() { const pids = []; const processes = ChromeUtils.getAllDOMProcesses(); for (const process of processes) { if (process.remoteType && process.remoteType.startsWith("web")) { pids.push(process.osPid); } } return pids; }, async killContentProcess(pid) { const procs = ChromeUtils.getAllDOMProcesses(); for (const proc of procs) { if (pid === proc.osPid) { proc .getActor("TestSupportProcess") .sendAsyncMessage("KillContentProcess"); } } }, async addHistogram(id, value) { return Services.telemetry.getHistogramById(id).add(value); }, removeAllCertOverrides() { const overrideService = Cc[ "@mozilla.org/security/certoverride;1" ].getService(Ci.nsICertOverrideService); overrideService.clearAllOverrides(); }, async setScalar(id, value) { return Services.telemetry.scalarSet(id, value); }, async setResolutionAndScaleTo(tabId, resolution) { return getActorForTab(tabId, "TestSupport").sendQuery( "SetResolutionAndScaleTo", { resolution, } ); }, async getActive(tabId) { const tab = context.extension.tabManager.get(tabId); return tab.browser.docShellIsActive; }, async getProfilePath() { return PathUtils.profileDir; }, async flushApzRepaints(tabId) { // TODO: Note that `waitUntilApzStable` in apz_test_utils.js does // flush APZ repaints in the parent process (i.e. calling // nsIDOMWindowUtils.flushApzRepaints for the parent process) before // flushApzRepaints is called for the target content document, if we // still meet intermittent failures, we might want to do it here as // well. await getActorForTab(tabId, "TestSupport").sendQuery( "FlushApzRepaints" ); }, async promiseAllPaintsDone(tabId) { await getActorForTab(tabId, "TestSupport").sendQuery( "PromiseAllPaintsDone" ); }, async usingGpuProcess() { const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService( Ci.nsIGfxInfo ); return gfxInfo.usingGPUProcess; }, async killGpuProcess() { const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService( Ci.nsIGfxInfo ); return gfxInfo.killGPUProcessForTests(); }, async crashGpuProcess() { const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService( Ci.nsIGfxInfo ); return gfxInfo.crashGPUProcessForTests(); }, async clearHSTSState() { const sss = Cc["@mozilla.org/ssservice;1"].getService( Ci.nsISiteSecurityService ); return sss.clearAll(); }, async triggerCookieBannerDetected(tabId) { const actor = getActorForTab(tabId, "CookieBanner"); return actor.receiveMessage({ name: "CookieBanner::DetectedBanner", }); }, async triggerCookieBannerHandled(tabId) { const actor = getActorForTab(tabId, "CookieBanner"); return actor.receiveMessage({ name: "CookieBanner::HandledBanner", }); }, async triggerTranslationsOffer(tabId) { const browser = context.extension.tabManager.get(tabId).browser; const { CustomEvent } = browser.ownerGlobal; return browser.dispatchEvent( new CustomEvent("TranslationsParent:OfferTranslation", { bubbles: true, }) ); }, async triggerLanguageStateChange(tabId, languageState) { const browser = context.extension.tabManager.get(tabId).browser; const { CustomEvent } = browser.ownerGlobal; return browser.dispatchEvent( new CustomEvent("TranslationsParent:LanguageState", { bubbles: true, detail: languageState, }) ); }, }, }; } };