forked from mirrors/gecko-dev
158 lines
5 KiB
JavaScript
158 lines
5 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
"use strict";
|
|
|
|
/* exported TestWorkerWatcher */
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
ExtensionCommon: "resource://gre/modules/ExtensionCommon.sys.mjs",
|
|
});
|
|
|
|
// 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.sys.mjs
|
|
// - data/TestWorkerWatcherChild.sys.mjs
|
|
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;
|
|
ChromeUtils.registerProcessActor(JS_ACTOR_NAME, {
|
|
parent: {
|
|
esModuleURI: `resource://testing-common/${JS_ACTOR_NAME}Parent.sys.mjs`,
|
|
},
|
|
child: {
|
|
esModuleURI: `resource://testing-common/${JS_ACTOR_NAME}Child.sys.mjs`,
|
|
},
|
|
});
|
|
}
|
|
|
|
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() {
|
|
// Keep the watched process and related test child process actor updated
|
|
// when a process is created or destroyed.
|
|
this.getAndWatchExtensionProcess();
|
|
}
|
|
}
|