mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 06:15:43 +00:00
Bug 982726 - Patch 3 - Implement client.postMessage and add tests for getServiced(). r=baku
--HG-- extra : rebase_source : 39ac96d409c4ea2ccf1794a7b2a0f681fc7c5786 extra : amend_source : 9357dd9eec14a5af6bd234189473d0bfdf5d6ba7
This commit is contained in:
parent
e77acd5d9e
commit
2abbc498cc
@ -11,4 +11,6 @@
|
||||
[Exposed=ServiceWorker]
|
||||
interface ServiceWorkerClient {
|
||||
readonly attribute unsigned long id;
|
||||
[Throws]
|
||||
void postMessage(any message, optional sequence<Transferable> transfer);
|
||||
};
|
||||
|
@ -6,8 +6,13 @@
|
||||
|
||||
#include "ServiceWorkerClient.h"
|
||||
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#include "mozilla/dom/ServiceWorkerClientBinding.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
@ -27,3 +32,128 @@ ServiceWorkerClient::WrapObject(JSContext* aCx)
|
||||
return ServiceWorkerClientBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ServiceWorkerClientPostMessageRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
uint64_t mId;
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
|
||||
public:
|
||||
ServiceWorkerClientPostMessageRunnable(uint64_t aId,
|
||||
JSAutoStructuredCloneBuffer&& aData,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
: mId(aId),
|
||||
mBuffer(Move(aData))
|
||||
{
|
||||
mClonedObjects.SwapElements(aClonedObjects);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mId);
|
||||
if (!window) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(window);
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
return DispatchDOMEvent(cx, window);
|
||||
}
|
||||
|
||||
private:
|
||||
NS_IMETHOD
|
||||
DispatchDOMEvent(JSContext* aCx, nsGlobalWindow* aTargetWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// Release reference to objects that were AddRef'd for
|
||||
// cloning into worker when array goes out of scope.
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
clonedObjects.SwapElements(mClonedObjects);
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
if (!mBuffer.read(aCx, &messageData,
|
||||
WorkerStructuredCloneCallbacks(true))) {
|
||||
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMMessageEvent> event = new MessageEvent(aTargetWindow,
|
||||
nullptr, nullptr);
|
||||
nsresult rv =
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* not cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(aCx, rv);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool status = false;
|
||||
aTargetWindow->DispatchEvent(event, &status);
|
||||
|
||||
if (!status) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
if (aTransferable.WasPassed()) {
|
||||
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
|
||||
|
||||
JS::HandleValueArray elements =
|
||||
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
|
||||
realTransferable.Elements());
|
||||
|
||||
JSObject* array = JS_NewArrayObject(aCx, elements);
|
||||
if (!array) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
|
||||
new ServiceWorkerClientPostMessageRunnable(mId, Move(buffer), clonedObjects);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,14 @@
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
template<typename T> class Optional;
|
||||
template<typename T> class Sequence;
|
||||
|
||||
namespace workers {
|
||||
|
||||
@ -36,6 +41,10 @@ public:
|
||||
return mId;
|
||||
}
|
||||
|
||||
void PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
|
@ -0,0 +1,12 @@
|
||||
onmessage = function(e) {
|
||||
if (!e.data) {
|
||||
dump("ERROR: message has no data.\n");
|
||||
}
|
||||
|
||||
self.clients.getServiced().then(function(res) {
|
||||
if (res.length === 0) {
|
||||
dump("ERROR: no client is currently being controlled.\n");
|
||||
}
|
||||
res[res.length - 1].postMessage(res.length);
|
||||
});
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
onmessage = function() {
|
||||
self.clients.getServiced().then(function(result) {
|
||||
for (i = 0; i < result.length; i++) {
|
||||
result[i].postMessage(i);
|
||||
}
|
||||
});
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
onmessage = function(e) {
|
||||
self.clients.getServiced().then(function(res) {
|
||||
if (!res.length) {
|
||||
dump("ERROR: no clients are currently controlled.\n");
|
||||
}
|
||||
res[0].postMessage(e.data);
|
||||
});
|
||||
};
|
||||
|
@ -12,11 +12,18 @@ support-files =
|
||||
unregister/index.html
|
||||
sw_clients/simple.html
|
||||
get_serviced_worker.js
|
||||
get_serviced_worker_advanced.js
|
||||
message_posting_worker.js
|
||||
sw_clients/service_worker_controlled.html
|
||||
get_serviced_worker_enumerate.js
|
||||
|
||||
[test_get_serviced.html]
|
||||
[test_get_serviced_advanced.html]
|
||||
[test_get_serviced_enumerate.html]
|
||||
[test_installation_simple.html]
|
||||
[test_install_event.html]
|
||||
[test_navigator.html]
|
||||
[test_post_message.html]
|
||||
[test_scopes.html]
|
||||
[test_controller.html]
|
||||
[test_unregister.html]
|
||||
|
@ -0,0 +1,39 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>controlled page</title>
|
||||
<!--
|
||||
Paged controlled by a service worker for testing getServiced().
|
||||
See bug 982726.
|
||||
-->
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
function fail(msg) {
|
||||
info("service_worker_controlled.html: " + msg);
|
||||
opener.postMessage("FAIL", "*");
|
||||
}
|
||||
|
||||
if (!opener) {
|
||||
info("service_worker_controlled.html should not be launched directly!");
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
navigator.serviceWorker.ready.then(function(swr) {
|
||||
opener.postMessage("READY", "*");
|
||||
});
|
||||
}
|
||||
|
||||
window.onmessage = function(msg) {
|
||||
// forward message to the test page.
|
||||
opener.postMessage(msg.data, "*");
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 982726 - test get_serviced </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
var opened = [];
|
||||
var registration;
|
||||
|
||||
function start() {
|
||||
return navigator.serviceWorker.register("get_serviced_worker_advanced.js",
|
||||
{ scope: "./sw_clients/" }).then((swr) => registration = swr);
|
||||
}
|
||||
|
||||
function testGetServiced() {
|
||||
var p = new Promise(function(res, rej) {
|
||||
window.onmessage = function(e) {
|
||||
if (e.data === "READY") {
|
||||
opened.push(w);
|
||||
registration.active.postMessage("getServiced");
|
||||
} else if (e.data === opened.length) {
|
||||
ok(true, "getServiced returned the correct value.");
|
||||
res();
|
||||
} else {
|
||||
ok(false, "Wrong value. Expected: " + opened.length +
|
||||
", got: " + e.data);
|
||||
res();
|
||||
}
|
||||
}
|
||||
});
|
||||
var w;
|
||||
setTimeout(function() {
|
||||
w = window.open("sw_clients/service_worker_controlled.html");
|
||||
}, 100);
|
||||
return p;
|
||||
}
|
||||
|
||||
function removeAndTest() {
|
||||
opened.pop().close();
|
||||
opened.pop().close();
|
||||
return testGetServiced();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
start()
|
||||
.then(testGetServiced)
|
||||
.then(testGetServiced)
|
||||
.then(testGetServiced)
|
||||
.then(removeAndTest)
|
||||
.then(function(e) {
|
||||
while (opened.length) {
|
||||
opened.pop().close();
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}).catch(function(e) {
|
||||
ok(false, "Some test failed with error " + e);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,82 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 982726 - test get_serviced </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
var registration;
|
||||
var opened = [];
|
||||
var results = [];
|
||||
|
||||
function start() {
|
||||
return navigator.serviceWorker.register("get_serviced_worker_enumerate.js",
|
||||
{ scope: "./sw_clients/" }).then((swr) => registration = swr);
|
||||
}
|
||||
|
||||
function openWindow() {
|
||||
var p = new Promise(function(res, rej) {
|
||||
var w = window.open("sw_clients/service_worker_controlled.html");
|
||||
window.onmessage = function(e) {
|
||||
if (e.data === "READY") {
|
||||
opened.push(w);
|
||||
res();
|
||||
}
|
||||
}
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
function testGetServiced() {
|
||||
var count = 0;
|
||||
return new Promise(function (res, rej) {
|
||||
registration.active.postMessage("getServiced");
|
||||
// wait for response
|
||||
window.onmessage = function(msg) {
|
||||
results[msg.data] = "ok";
|
||||
count = count + 1;
|
||||
if (count == opened.length) {
|
||||
for (i = 0; i < count; i++) {
|
||||
ok(results[i] == "ok", "Client received the message.");
|
||||
}
|
||||
res();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
start()
|
||||
.then(openWindow).then(openWindow)
|
||||
.then(openWindow).then(openWindow)
|
||||
.then(testGetServiced)
|
||||
.then(function(e) {
|
||||
while (opened.length) {
|
||||
opened.pop().close();
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}).catch(function(e) {
|
||||
ok(false, "Some test failed with error " + e);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
62
dom/workers/test/serviceworkers/test_post_message.html
Normal file
62
dom/workers/test/serviceworkers/test_post_message.html
Normal file
@ -0,0 +1,62 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 982726 - Test service worker post message </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
var magic_value = "MAGIC_VALUE_123";
|
||||
function start() {
|
||||
return navigator.serviceWorker.register("message_posting_worker.js",
|
||||
{ scope: "./sw_clients/" });
|
||||
}
|
||||
|
||||
function testPostMessage(swr) {
|
||||
var p = new Promise(function(res, rej) {
|
||||
window.onmessage = function(e) {
|
||||
if (e.data === "READY") {
|
||||
swr.active.postMessage(magic_value);
|
||||
} else if (e.data === magic_value) {
|
||||
ok(true, "Worker posted the correct value.");
|
||||
res();
|
||||
} else {
|
||||
ok(false, "Wrong value. Expected: " + magic_value +
|
||||
", got: " + e.data);
|
||||
res();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var w;
|
||||
setTimeout(function() {
|
||||
w = window.open("sw_clients/service_worker_controlled.html");
|
||||
}, 100);
|
||||
return p.then(() => w.close());
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
start()
|
||||
.then(testPostMessage).catch(function(e) {
|
||||
ok(false, "Some test failed with error " + e);
|
||||
}).then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user