mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Bug 1209699 - Add a 'Push' button for service workers in about:debugging. r=jdescottes
This commit is contained in:
parent
634d246572
commit
f38239f17f
@ -51,6 +51,7 @@ button {
|
||||
|
||||
.target {
|
||||
margin-top: 5px;
|
||||
min-height: 34px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
@ -38,6 +38,13 @@ module.exports = createClass({
|
||||
dom.div({ className: "target-details" },
|
||||
dom.div({ className: "target-name" }, target.name)
|
||||
),
|
||||
(isRunning && isServiceWorker ?
|
||||
dom.button({
|
||||
className: "push-button",
|
||||
onClick: this.push
|
||||
}, Strings.GetStringFromName("push")) :
|
||||
null
|
||||
),
|
||||
(isRunning ?
|
||||
dom.button({
|
||||
className: "debug-button",
|
||||
@ -72,6 +79,16 @@ module.exports = createClass({
|
||||
}
|
||||
},
|
||||
|
||||
push() {
|
||||
let { client, target } = this.props;
|
||||
if (target.workerActor) {
|
||||
client.request({
|
||||
to: target.workerActor,
|
||||
type: "push"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
openWorkerToolbox(workerActor) {
|
||||
let { client } = this.props;
|
||||
client.attachWorker(workerActor, (response, workerClient) => {
|
||||
|
@ -7,9 +7,12 @@ support-files =
|
||||
addons/unpacked/install.rdf
|
||||
service-workers/empty-sw.html
|
||||
service-workers/empty-sw.js
|
||||
service-workers/push-sw.html
|
||||
service-workers/push-sw.js
|
||||
|
||||
[browser_addons_debugging_initial_state.js]
|
||||
[browser_addons_install.js]
|
||||
[browser_addons_toggle_debug.js]
|
||||
[browser_service_workers.js]
|
||||
[browser_service_workers_push.js]
|
||||
[browser_service_workers_timeout.js]
|
||||
|
@ -0,0 +1,98 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
/* global sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that clicking on the Push button next to a Service Worker works as
|
||||
// intended in about:debugging.
|
||||
// It should trigger a "push" notification in the worker.
|
||||
|
||||
// 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/push-sw.js";
|
||||
const TAB_URL = HTTP_ROOT + "service-workers/push-sw.html";
|
||||
|
||||
add_task(function* () {
|
||||
info("Turn on workers via mochitest http.");
|
||||
yield new Promise(done => {
|
||||
let options = { "set": [
|
||||
// Accept workers from mochitest's http.
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]};
|
||||
SpecialPowers.pushPrefEnv(options, done);
|
||||
});
|
||||
|
||||
let { tab, document } = yield openAboutDebugging("workers");
|
||||
|
||||
// Listen for mutations in the service-workers list.
|
||||
let serviceWorkersElement = document.getElementById("service-workers");
|
||||
let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
|
||||
|
||||
// Open a tab that registers a push service worker.
|
||||
let swTab = yield addTab(TAB_URL);
|
||||
|
||||
info("Make the test page notify us when the service worker sends a message.");
|
||||
let frameScript = function() {
|
||||
let win = content.wrappedJSObject;
|
||||
win.navigator.serviceWorker.addEventListener("message", function(event) {
|
||||
sendAsyncMessage(event.data);
|
||||
}, false);
|
||||
};
|
||||
let mm = swTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
// Expect the service worker to claim the test window when activating.
|
||||
let onClaimed = new Promise(done => {
|
||||
mm.addMessageListener("sw-claimed", function listener() {
|
||||
mm.removeMessageListener("sw-claimed", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the service-workers list to update.
|
||||
yield onMutation;
|
||||
|
||||
// Check that the service worker appears in the UI.
|
||||
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
|
||||
|
||||
info("Ensure that the registration resolved before trying to interact with " +
|
||||
"the service worker.");
|
||||
yield waitForServiceWorkerRegistered(swTab);
|
||||
ok(true, "Service worker registration resolved");
|
||||
|
||||
// Retrieve the Push 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 targetElement = name.parentNode.parentNode;
|
||||
let pushBtn = targetElement.querySelector(".push-button");
|
||||
ok(pushBtn, "Found its push button");
|
||||
|
||||
info("Wait for the service worker to claim the test window before " +
|
||||
"proceeding.");
|
||||
yield onClaimed;
|
||||
|
||||
info("Click on the Push button and wait for the service worker to receive " +
|
||||
"a push notification");
|
||||
let onPushNotification = new Promise(done => {
|
||||
mm.addMessageListener("sw-pushed", function listener() {
|
||||
mm.removeMessageListener("sw-pushed", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
pushBtn.click();
|
||||
yield onPushNotification;
|
||||
ok(true, "Service worker received a push notification");
|
||||
|
||||
// Finally, unregister the service worker itself.
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
|
||||
yield removeTab(swTab);
|
||||
yield closeAboutDebugging(tab);
|
||||
});
|
@ -1,9 +1,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
/* global sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Service workers can't be loaded from chrome://,
|
||||
@ -15,13 +12,6 @@ const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
|
||||
|
||||
const SW_TIMEOUT = 1000;
|
||||
|
||||
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": [
|
||||
@ -42,25 +32,10 @@ add_task(function* () {
|
||||
let serviceWorkersElement = document.getElementById("service-workers");
|
||||
yield waitForMutation(serviceWorkersElement, { childList: true });
|
||||
|
||||
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
|
||||
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
|
||||
|
||||
// Ensure that the registration resolved before trying to connect to the sw
|
||||
let frameScript = function() {
|
||||
// Retrieve the `sw` promise created in the html page
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function() {
|
||||
sendAsyncMessage("sw-registered");
|
||||
});
|
||||
};
|
||||
let mm = swTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
yield new Promise(done => {
|
||||
mm.addMessageListener("sw-registered", function listener() {
|
||||
mm.removeMessageListener("sw-registered", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
yield waitForServiceWorkerRegistered(swTab);
|
||||
ok(true, "Service worker registration resolved");
|
||||
|
||||
// Retrieve the DEBUG button for the worker
|
||||
@ -88,7 +63,7 @@ add_task(function* () {
|
||||
setTimeout(done, SW_TIMEOUT * 2);
|
||||
});
|
||||
|
||||
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
|
||||
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
|
||||
ok(targetElement.querySelector(".debug-button"),
|
||||
"The debug button is still there");
|
||||
|
||||
@ -102,35 +77,14 @@ add_task(function* () {
|
||||
ok(!targetElement.querySelector(".debug-button"),
|
||||
"The debug button was removed when the worker was killed");
|
||||
|
||||
// Finally, unregister the service worker itself
|
||||
// Use message manager to work with e10s
|
||||
frameScript = function() {
|
||||
// Retrieve the `sw` promise created in the html page
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function(registration) {
|
||||
registration.unregister().then(function() {
|
||||
sendAsyncMessage("sw-unregistered");
|
||||
},
|
||||
function(e) {
|
||||
dump("SW not unregistered; " + e + "\n");
|
||||
});
|
||||
});
|
||||
};
|
||||
mm = swTab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
yield new Promise(done => {
|
||||
mm.addMessageListener("sw-unregistered", function listener() {
|
||||
mm.removeMessageListener("sw-unregistered", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
// Finally, unregister the service worker itself.
|
||||
yield unregisterServiceWorker(swTab);
|
||||
ok(true, "Service worker registration unregistered");
|
||||
|
||||
// Now ensure that the worker registration is correctly removed.
|
||||
// The list should update once the registration is destroyed.
|
||||
yield waitForMutation(serviceWorkersElement, { childList: true });
|
||||
assertHasWorker(false, document, "service-workers", SERVICE_WORKER);
|
||||
assertHasTarget(false, document, "service-workers", SERVICE_WORKER);
|
||||
|
||||
yield removeTab(swTab);
|
||||
yield closeAboutDebugging(tab);
|
||||
|
@ -2,15 +2,18 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-env browser */
|
||||
/* eslint-disable mozilla/no-cpows-in-tests */
|
||||
/* exported openAboutDebugging, closeAboutDebugging, installAddon,
|
||||
uninstallAddon, waitForMutation */
|
||||
uninstallAddon, waitForMutation, assertHasTarget,
|
||||
waitForServiceWorkerRegistered, unregisterServiceWorker */
|
||||
/* global sendAsyncMessage */
|
||||
|
||||
"use strict";
|
||||
|
||||
var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
|
||||
var { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
const Services = require("Services");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
DevToolsUtils.testing = true;
|
||||
@ -146,3 +149,73 @@ function waitForMutation(target, mutationOptions) {
|
||||
observer.observe(target, mutationOptions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an about:debugging TargetList element contains a Target element
|
||||
* corresponding to the specified name.
|
||||
* @param {Boolean} expected
|
||||
* @param {Document} document
|
||||
* @param {String} type
|
||||
* @param {String} name
|
||||
*/
|
||||
function assertHasTarget(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve after the service worker in the page
|
||||
* has successfully registered itself.
|
||||
* @param {Tab} tab
|
||||
*/
|
||||
function waitForServiceWorkerRegistered(tab) {
|
||||
// Make the test page notify us when the service worker is registered.
|
||||
let frameScript = function() {
|
||||
// Retrieve the `sw` promise created in the html page.
|
||||
let { sw } = content.wrappedJSObject;
|
||||
sw.then(function(registration) {
|
||||
sendAsyncMessage("sw-registered");
|
||||
});
|
||||
};
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
return new Promise(done => {
|
||||
mm.addMessageListener("sw-registered", function listener() {
|
||||
mm.removeMessageListener("sw-registered", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the service worker within the test page to unregister, and returns a
|
||||
* promise that will resolve when it has successfully unregistered itself.
|
||||
* @param {Tab} tab
|
||||
*/
|
||||
function unregisterServiceWorker(tab) {
|
||||
// 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() {
|
||||
sendAsyncMessage("sw-unregistered");
|
||||
},
|
||||
function(e) {
|
||||
dump("SW not unregistered; " + e + "\n");
|
||||
});
|
||||
});
|
||||
};
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
|
||||
|
||||
return new Promise(done => {
|
||||
mm.addMessageListener("sw-unregistered", function listener() {
|
||||
mm.removeMessageListener("sw-unregistered", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Service worker push test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
var sw = navigator.serviceWorker.register("push-sw.js");
|
||||
sw.then(
|
||||
function(registration) {
|
||||
dump("SW registered\n");
|
||||
},
|
||||
function(error) {
|
||||
dump("SW not registered: " + error + "\n");
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,33 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-env worker */
|
||||
/* global clients */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Send a message to all controlled windows.
|
||||
function postMessage(message) {
|
||||
return clients.matchAll().then(function(clientlist) {
|
||||
clientlist.forEach(function(client) {
|
||||
client.postMessage(message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Don't wait for the next page load to become the active service worker.
|
||||
self.addEventListener("install", function(event) {
|
||||
event.waitUntil(self.skipWaiting());
|
||||
});
|
||||
|
||||
// Claim control over the currently open test page when activating.
|
||||
self.addEventListener("activate", function(event) {
|
||||
event.waitUntil(self.clients.claim().then(function() {
|
||||
return postMessage("sw-claimed");
|
||||
}));
|
||||
});
|
||||
|
||||
// Forward all "push" events to the controlled window.
|
||||
self.addEventListener("push", function(event) {
|
||||
event.waitUntil(postMessage("sw-pushed"));
|
||||
});
|
@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
debug = Debug
|
||||
push = Push
|
||||
|
||||
addons = Add-ons
|
||||
addonDebugging.label = Enable add-on debugging
|
||||
|
@ -5,6 +5,8 @@ var { DebuggerServer } = require("devtools/server/main");
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { Arg, method, RetVal } = protocol;
|
||||
|
||||
loader.lazyRequireGetter(this, "ChromeUtils");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
@ -140,6 +142,20 @@ let WorkerActor = protocol.ActorClass({
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
push: method(function () {
|
||||
if (this._dbg.type !== Ci.nsIWorkerDebugger.TYPE_SERVICE) {
|
||||
return { error: "wrongType" };
|
||||
}
|
||||
let registration = this._getServiceWorkerRegistrationInfo();
|
||||
let originAttributes = ChromeUtils.originAttributesToSuffix(
|
||||
this._dbg.principal.originAttributes);
|
||||
swm.sendPushEvent(originAttributes, registration.scope);
|
||||
return { type: "pushed" };
|
||||
}, {
|
||||
request: {},
|
||||
response: RetVal("json")
|
||||
}),
|
||||
|
||||
onClose: function () {
|
||||
if (this._attached) {
|
||||
this._detach();
|
||||
|
@ -32,6 +32,7 @@ var loaderModules = {
|
||||
"Services": Object.create(Services),
|
||||
"toolkit/loader": Loader,
|
||||
PromiseDebugging,
|
||||
ChromeUtils,
|
||||
ThreadSafeChromeUtils,
|
||||
HeapSnapshot,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user