forked from mirrors/gecko-dev
This patch includes: - a new xpcshell-serviceworker.ini manifest, all tests included in this manifest will also run in the "background service worker mode", initially including all `alarms` API xpcshell tests - some small changes to test_ext_alarms.js to temporarily skip test case that can't yet pass while running in "background service worker mode" (until fixed in separate followups) - some small tweaks to head_service_worker.js to avoid failure due to an existing ExtensionCommon const bindings being redefined while running a test for the xpcshell-remote.ini manifest Differential Revision: https://phabricator.services.mozilla.com/D135125
161 lines
5.1 KiB
JavaScript
161 lines
5.1 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
"use strict";
|
|
|
|
/* exported TestWorkerWatcher */
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
ExtensionCommon: "resource://gre/modules/ExtensionCommon.jsm",
|
|
});
|
|
|
|
// Ensure that the profile-after-change message has been notified,
|
|
// so that ServiceWokerRegistrar is going to be initialized,
|
|
// otherwise tests using a background service worker will fail.
|
|
// in debug builds because of an assertion failure triggered
|
|
// by ServiceWorkerRegistrar.cpp (due to not being initialized
|
|
// automatically on startup as in a real Firefox instance).
|
|
Services.obs.notifyObservers(
|
|
null,
|
|
"profile-after-change",
|
|
"force-serviceworkerrestart-init"
|
|
);
|
|
|
|
// A test utility class used in the test case to watch for a given extension
|
|
// service worker being spawned and terminated (using the same kind of Firefox DevTools
|
|
// internals that about:debugging is using to watch the workers activity).
|
|
//
|
|
// NOTE: this helper class does also depends from the two jsm files where the
|
|
// Parent and Child TestWorkerWatcher actor is defined:
|
|
//
|
|
// - data/TestWorkerWatcherParent.jsm
|
|
// - data/TestWorkerWatcherChild.jsm
|
|
class TestWorkerWatcher extends ExtensionCommon.EventEmitter {
|
|
JS_ACTOR_NAME = "TestWorkerWatcher";
|
|
|
|
constructor(dataRelPath = "./data") {
|
|
super();
|
|
this.dataRelPath = dataRelPath;
|
|
this.extensionProcess = null;
|
|
this.extensionProcessActor = null;
|
|
this.registerProcessActor();
|
|
this.getAndWatchExtensionProcess();
|
|
// Observer child process creation and shutdown if the extension
|
|
// are meant to run in a child process.
|
|
Services.obs.addObserver(this, "ipc:content-created");
|
|
Services.obs.addObserver(this, "ipc:content-shutdown");
|
|
}
|
|
|
|
async destroy() {
|
|
await this.stopWatchingWorkers();
|
|
ChromeUtils.unregisterProcessActor(this.JS_ACTOR_NAME);
|
|
}
|
|
|
|
get swm() {
|
|
return Cc["@mozilla.org/serviceworkers/manager;1"].getService(
|
|
Ci.nsIServiceWorkerManager
|
|
);
|
|
}
|
|
|
|
getRegistration(extension) {
|
|
return this.swm.getRegistrationByPrincipal(
|
|
extension.extension.principal,
|
|
extension.extension.principal.spec
|
|
);
|
|
}
|
|
|
|
watchExtensionServiceWorker(extension) {
|
|
// These events are emitted by TestWatchExtensionWorkersParent.
|
|
const promiseWorkerSpawned = this.waitForEvent("worker-spawned", extension);
|
|
const promiseWorkerTerminated = this.waitForEvent(
|
|
"worker-terminated",
|
|
extension
|
|
);
|
|
|
|
// Terminate the worker sooner by settng the idle_timeout to 0,
|
|
// then clear the pref as soon as the worker has been terminated.
|
|
const terminate = () => {
|
|
promiseWorkerTerminated.then(() => {
|
|
Services.prefs.clearUserPref("dom.serviceWorkers.idle_timeout");
|
|
});
|
|
Services.prefs.setIntPref("dom.serviceWorkers.idle_timeout", 0);
|
|
const swReg = this.getRegistration(extension);
|
|
// If the active worker is already active, we have to make sure the new value
|
|
// set on the idle_timeout pref is picked up by ServiceWorkerPrivate::ResetIdleTimeout.
|
|
swReg.activeWorker?.attachDebugger();
|
|
swReg.activeWorker?.detachDebugger();
|
|
return promiseWorkerTerminated;
|
|
};
|
|
|
|
return {
|
|
promiseWorkerSpawned,
|
|
promiseWorkerTerminated,
|
|
terminate,
|
|
};
|
|
}
|
|
|
|
// Methods only used internally.
|
|
|
|
waitForEvent(event, extension) {
|
|
return new Promise(resolve => {
|
|
const listener = (_eventName, data) => {
|
|
if (!data.workerUrl.startsWith(extension.extension?.principal.spec)) {
|
|
return;
|
|
}
|
|
this.off(event, listener);
|
|
resolve(data);
|
|
};
|
|
|
|
this.on(event, listener);
|
|
});
|
|
}
|
|
|
|
registerProcessActor() {
|
|
const { JS_ACTOR_NAME } = this;
|
|
const getModuleURI = fileName =>
|
|
Services.io.newFileURI(do_get_file(`${this.dataRelPath}/${fileName}`))
|
|
.spec;
|
|
ChromeUtils.registerProcessActor(JS_ACTOR_NAME, {
|
|
parent: {
|
|
moduleURI: getModuleURI(`${JS_ACTOR_NAME}Parent.jsm`),
|
|
},
|
|
child: {
|
|
moduleURI: getModuleURI(`${JS_ACTOR_NAME}Child.jsm`),
|
|
},
|
|
});
|
|
}
|
|
|
|
startWatchingWorkers() {
|
|
if (!this.extensionProcessActor) {
|
|
return;
|
|
}
|
|
this.extensionProcessActor.eventEmitter = this;
|
|
return this.extensionProcessActor.sendQuery("Test:StartWatchingWorkers");
|
|
}
|
|
|
|
stopWatchingWorkers() {
|
|
if (!this.extensionProcessActor) {
|
|
return;
|
|
}
|
|
this.extensionProcessActor.eventEmitter = null;
|
|
return this.extensionProcessActor.sendQuery("Test:StopWatchingWorkers");
|
|
}
|
|
|
|
getAndWatchExtensionProcess() {
|
|
const extensionProcess = ChromeUtils.getAllDOMProcesses().find(p => {
|
|
return p.remoteType === "extension";
|
|
});
|
|
if (extensionProcess !== this.extensionProcess) {
|
|
this.extensionProcess = extensionProcess;
|
|
this.extensionProcessActor = extensionProcess
|
|
? extensionProcess.getActor(this.JS_ACTOR_NAME)
|
|
: null;
|
|
this.startWatchingWorkers();
|
|
}
|
|
}
|
|
|
|
observe(subject, topic, childIDString) {
|
|
// Keep the watched process and related test child process actor updated
|
|
// when a process is created or destroyed.
|
|
this.getAndWatchExtensionProcess();
|
|
}
|
|
}
|