Bug 1228382 - Keep service worker alive when attaching to them. r=janx,ejpbruel

This commit is contained in:
Alexandre Poirot 2015-12-15 03:10:53 -08:00
parent 5fb8101bee
commit 051fd6c3a9
4 changed files with 150 additions and 1 deletions

View File

@ -41,7 +41,10 @@ exports.TargetComponent = React.createClass({
let workerActor = this.props.target.actorID;
client.attachWorker(workerActor, (response, workerClient) => {
gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
"jsdebugger", Toolbox.HostType.WINDOW);
"jsdebugger", Toolbox.HostType.WINDOW)
.then(toolbox => {
toolbox.once("destroy", () => workerClient.detach());
});
});
break;
default:

View File

@ -10,3 +10,4 @@ support-files =
[browser_addons_install.js]
[browser_service_workers.js]
[browser_service_workers_timeout.js]

View File

@ -0,0 +1,112 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Service workers can't be loaded from chrome://,
// but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
const HTTP_ROOT = CHROME_ROOT.replace("chrome://mochitests/content/",
"http://mochi.test:8888/");
const SERVICE_WORKER = HTTP_ROOT + "service-workers/empty-sw.js";
const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
const SW_TIMEOUT = 1000;
function waitForWorkersUpdate(document) {
return new Promise(done => {
var observer = new MutationObserver(function(mutations) {
observer.disconnect();
done();
});
var target = document.getElementById("service-workers");
observer.observe(target, { childList: true });
});
}
function assertHasWorker(expected, document, type, name) {
let names = [...document.querySelectorAll("#" + type + " .target-name")];
names = names.map(element => element.textContent);
is(names.includes(name), expected, "The " + type + " url appears in the list: " + names);
}
add_task(function *() {
yield new Promise(done => {
let options = {"set": [
// Accept workers from mochitest's http
["dom.serviceWorkers.testing.enabled", true],
// Reduce the timeout to expose issues when service worker
// freezing is broken
["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
]};
SpecialPowers.pushPrefEnv(options, done);
});
let { tab, document } = yield openAboutDebugging("workers");
let swTab = yield addTab(TAB_URL);
yield waitForWorkersUpdate(document);
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
// XXX: race, the WorkerDebugger is ready whereas ServiceWorkerInfo
// doesn't has the worker registered yet on its side
yield new Promise(done => {
require("sdk/timers").setTimeout(done, 250);
});
// Retrieve the DEBUG button for the worker
let names = [...document.querySelectorAll("#service-workers .target-name")];
let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
ok(name, "Found the service worker in the list");
let debugBtn = name.parentNode.parentNode.querySelector("button");
ok(debugBtn, "Found its debug button");
// Click on it and wait for the toolbox to be ready
let onToolboxReady = new Promise(done => {
gDevTools.once("toolbox-ready", function (e, toolbox) {
done(toolbox);
});
});
debugBtn.click();
let toolbox = yield onToolboxReady;
// Wait for more than the regular timeout,
// so that if the worker freezing doesn't work,
// it will be destroyed and removed from the list
yield new Promise(done => {
setTimeout(done, SW_TIMEOUT * 2);
});
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
yield toolbox.destroy();
toolbox = null;
// Now ensure that the worker is correctly destroyed
// after we destroy the toolbox.
// The list should update once it get destroyed.
yield waitForWorkersUpdate(document);
assertHasWorker(false, document, "service-workers", SERVICE_WORKER);
// Finally, unregister the service worker itself
// Use message manager to work with e10s
let frameScript = function () {
// Retrieve the `sw` promise created in the html page
let { sw } = content.wrappedJSObject;
sw.then(function (registration) {
registration.unregister().then(function (success) {
dump("SW unregistered: " + success + "\n");
},
function (e) {
dump("SW not unregistered; " + e + "\n");
});
});
};
swTab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
yield removeTab(swTab);
yield closeAboutDebugging(tab);
});

View File

@ -11,6 +11,12 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIWorkerDebuggerManager"
);
XPCOMUtils.defineLazyServiceGetter(
this, "swm",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager"
);
function matchWorkerDebugger(dbg, options) {
if ("type" in options && dbg.type !== options.type) {
return false;
@ -54,6 +60,14 @@ WorkerActor.prototype = {
}
if (!this._isAttached) {
// Automatically disable their internal timeout that shut them down
// Should be refactored by having actors specific to service workers
if (this._dbg.type == Ci.nsIWorkerDebugger.TYPE_SERVICE) {
let worker = this._getServiceWorkerInfo();
if (worker) {
worker.attachDebugger();
}
}
this._dbg.addListener(this);
this._isAttached = true;
}
@ -115,6 +129,11 @@ WorkerActor.prototype = {
reportError("ERROR:" + filename + ":" + lineno + ":" + message + "\n");
},
_getServiceWorkerInfo: function () {
let info = swm.getRegistrationByPrincipal(this._dbg.principal, this._dbg.url);
return info.getWorkerByID(this._dbg.serviceWorkerID);
},
_detach: function () {
if (this._threadActor !== null) {
this._transport.close();
@ -122,6 +141,20 @@ WorkerActor.prototype = {
this._threadActor = null;
}
// If the worker is already destroyed, nsIWorkerDebugger.type throws
// (_dbg.closed appears to be false when it throws)
let type;
try {
type = this._dbg.type;
} catch(e) {}
if (type == Ci.nsIWorkerDebugger.TYPE_SERVICE) {
let worker = this._getServiceWorkerInfo();
if (worker) {
worker.detachDebugger();
}
}
this._dbg.removeListener(this);
this._isAttached = false;
}