Commit f7777559 authored by kinuko@chromium.org's avatar kinuko@chromium.org

Add blink-side binding code and tests for ServiceWorker -> Document postMessage

This is the last patch for postMessage plumbing:
1/3: https://codereview.chromium.org/263143004/ (blink)
2/3: https://codereview.chromium.org/246023007/ (chromium)
3/3: THIS PATCH

BUG=366063
TEST=http/tests/serviceworker/postmessage-to-client.html
R=jsbell@chromium.org, tkent@chromium.org

Review URL: https://codereview.chromium.org/264233003

git-svn-id: svn://svn.chromium.org/blink/trunk@173736 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent b7ad6bec
FAIL postMessage MessagePorts from ServiceWorker to Client assert_unreached: Unregister should not fail: NotSupportedError Reached unreachable code
<!DOCTYPE html>
<title>Service Worker: postMessage to Client</title>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="resources/test-helpers.js"></script>
<script>
var t = async_test('postMessage MessagePorts from ServiceWorker to Client');
t.step(function() {
var scope = 'resources/blank.html'
navigator.serviceWorker.unregister(scope).then(
doRegister,
unreached_rejection(t, 'Unregister should not fail')
);
function doRegister() {
navigator.serviceWorker.register(
'resources/postmessage-msgport-to-client-worker.js', {scope: scope}
).then(
onRegister,
unreached_rejection(t, 'Registration should succeed, but failed')
);
}
function onRegister(worker) {
worker.addEventListener('statechange', t.step_func(function(event) {
if (event.target.state == 'active')
onActive();
}));
}
function onActive() {
with_iframe(scope, t.step_func(function(frame) {
var w = frame.contentWindow;
w.onmessage = t.step_func(onMessage);
w.navigator.serviceWorker.current.postMessage('ping');
}));
}
var result = [];
var expected = [
'Acking value: 1',
'Acking value: 2',
];
function onMessage(e) {
var message = e.data;
if ('port' in message) {
var port = message.port;
port.postMessage({value: 1});
port.postMessage({value: 2});
port.postMessage({done: true});
} else if ('ack' in message) {
result.push(message.ack);
} else if ('done' in message) {
assert_array_equals(result, expected, 'Worker should post back expected values via MessagePort.');
t.done();
} else {
assert_unreached('Got unexpected message from ServiceWorker');
}
}
});
</script>
FAIL postMessage from ServiceWorker to Client assert_unreached: Unregister should not fail: NotSupportedError Reached unreachable code
<!DOCTYPE html>
<title>Service Worker: postMessage to Client</title>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="resources/test-helpers.js"></script>
<script>
var t = async_test('postMessage from ServiceWorker to Client');
t.step(function() {
var scope = 'resources/blank.html'
navigator.serviceWorker.unregister(scope).then(
doRegister,
unreached_rejection(t, 'Unregister should not fail')
);
function doRegister() {
navigator.serviceWorker.register(
'resources/postmessage-to-client-worker.js', {scope: scope}
).then(
onRegister,
unreached_rejection(t, 'Registration should succeed, but failed')
);
}
function onRegister(worker) {
worker.addEventListener('statechange', t.step_func(function(event) {
if (event.target.state == 'active')
onActive();
}));
}
function onActive() {
with_iframe(scope, t.step_func(function(frame) {
var w = frame.contentWindow;
w.onmessage = t.step_func(onMessage);
w.navigator.serviceWorker.current.postMessage('ping');
}));
}
var result = [];
var expected = ['Sending message via clients'];
function onMessage(e) {
var message = e.data;
if (message === 'quit') {
assert_array_equals(result, expected, 'Worker should post back expected messages.');
t.done();
} else {
result.push(message);
}
}
});
</script>
self.onmessage = function(e) {
self.clients.getServiced().then(function(clients) {
clients.forEach(function(client) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = onMessageViaMessagePort.bind(null, client);
client.postMessage({port: messageChannel.port2}, [messageChannel.port2]);
});
});
};
function onMessageViaMessagePort(client, e) {
var message = e.data;
if ('value' in message) {
client.postMessage({ack: 'Acking value: ' + message.value});
} else if ('done' in message) {
client.postMessage({done: true});
}
}
self.onmessage = function(e) {
self.clients.getServiced().then(function(clients) {
clients.forEach(function(client) {
client.postMessage('Sending message via clients');
client.postMessage('quit');
});
});
};
......@@ -161,6 +161,7 @@
'v8/custom/V8CSSStyleDeclarationCustom.cpp',
'v8/custom/V8CSSValueCustom.cpp',
'v8/custom/V8CanvasRenderingContext2DCustom.cpp',
'v8/custom/V8ClientCustom.cpp',
'v8/custom/V8CryptoCustom.cpp',
'v8/custom/V8CustomEventCustom.cpp',
'v8/custom/V8CustomSQLStatementErrorCallback.cpp',
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "V8Client.h"
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/SerializedScriptValue.h"
#include "bindings/v8/V8Binding.h"
#include "core/dom/MessagePort.h"
#include "modules/serviceworkers/ServiceWorker.h"
#include "wtf/ArrayBuffer.h"
namespace WebCore {
void V8Client::postMessageMethodCustom(const v8::FunctionCallbackInfo<v8::Value>& info)
{
ExceptionState exceptionState(ExceptionState::ExecutionContext, "postMessage", "ServiceWorker", info.Holder(), info.GetIsolate());
Client* client = V8Client::toNative(info.Holder());
MessagePortArray ports;
ArrayBufferArray arrayBuffers;
if (info.Length() > 1) {
const int transferablesArgIndex = 1;
if (!SerializedScriptValue::extractTransferables(info[transferablesArgIndex], transferablesArgIndex, ports, arrayBuffers, exceptionState, info.GetIsolate())) {
exceptionState.throwIfNeeded();
return;
}
}
RefPtr<SerializedScriptValue> message = SerializedScriptValue::create(info[0], &ports, &arrayBuffers, exceptionState, info.GetIsolate());
if (exceptionState.throwIfNeeded())
return;
ExecutionContext* context = currentExecutionContext(info.GetIsolate());
client->postMessage(context, message.release(), &ports, exceptionState);
exceptionState.throwIfNeeded();
}
} // namespace WebCore
......@@ -26,7 +26,7 @@
[
CustomConstructor,
Exposed=Window&Worker
Exposed=Window&Worker&ServiceWorker,
] interface MessageChannel {
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
......
......@@ -5,6 +5,10 @@
#include "config.h"
#include "modules/serviceworkers/Client.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/SerializedScriptValue.h"
#include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
#include "public/platform/WebString.h"
#include "wtf/RefPtr.h"
namespace WebCore {
......@@ -24,4 +28,16 @@ Client::~Client()
{
}
void Client::postMessage(ExecutionContext* context, PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionState& exceptionState)
{
// Disentangle the port in preparation for sending it to the remote context.
OwnPtr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, exceptionState);
if (exceptionState.hadException())
return;
blink::WebString messageString = message->toWireString();
OwnPtr<blink::WebMessagePortChannelArray> webChannels = MessagePort::toWebMessagePortChannelArray(channels.release());
ServiceWorkerGlobalScopeClient::from(context)->postMessageToClient(m_id, messageString, webChannels.release());
}
} // namespace WebCore
......@@ -6,6 +6,7 @@
#define Client_h
#include "bindings/v8/ScriptWrappable.h"
#include "bindings/v8/SerializedScriptValue.h"
#include "wtf/Forward.h"
namespace WebCore {
......@@ -17,6 +18,7 @@ public:
// Client.idl
unsigned id() const { return m_id; }
void postMessage(ExecutionContext*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray*, ExceptionState&);
private:
explicit Client(unsigned id);
......
......@@ -7,4 +7,9 @@
RuntimeEnabled=ServiceWorker
] interface Client {
readonly attribute unsigned long id;
// FIXME: Currently we think targetOrigin's probably not necessary, but
// re-add it if the ongoing issue [1] concludes the other way.
// [1] https://github.com/slightlyoff/ServiceWorker/issues/190
[Custom, RaisesException, CallWith=ExecutionContext] void postMessage(SerializedScriptValue message, optional MessagePort[] messagePorts);
};
......@@ -31,8 +31,10 @@
#ifndef ServiceWorkerGlobalScopeClient_h
#define ServiceWorkerGlobalScopeClient_h
#include "core/dom/MessagePort.h"
#include "core/workers/WorkerClients.h"
#include "public/platform/WebCallbacks.h"
#include "public/platform/WebMessagePortChannel.h"
#include "public/platform/WebServiceWorkerClientsInfo.h"
#include "public/platform/WebServiceWorkerEventResult.h"
#include "wtf/Forward.h"
......@@ -61,6 +63,7 @@ public:
// A null response means no valid response was provided by the service worker, so fallback to native.
virtual void didHandleFetchEvent(int fetchEventID, PassRefPtr<Response> = nullptr) = 0;
virtual void didHandleSyncEvent(int syncEventID) = 0;
virtual void postMessageToClient(int clientID, const blink::WebString& message, PassOwnPtr<blink::WebMessagePortChannelArray>) = 0;
static const char* supplementName();
static ServiceWorkerGlobalScopeClient* from(ExecutionContext*);
......
......@@ -88,6 +88,11 @@ void ServiceWorkerGlobalScopeClientImpl::didHandleSyncEvent(int syncEventID)
m_client.didHandleSyncEvent(syncEventID);
}
void ServiceWorkerGlobalScopeClientImpl::postMessageToClient(int clientID, const WebString& message, PassOwnPtr<WebMessagePortChannelArray> webChannels)
{
m_client.postMessageToClient(clientID, message, webChannels.leakPtr());
}
ServiceWorkerGlobalScopeClientImpl::ServiceWorkerGlobalScopeClientImpl(WebServiceWorkerContextClient& client)
: m_client(client)
{
......
......@@ -53,6 +53,7 @@ public:
virtual void didHandleInstallEvent(int installEventID, WebServiceWorkerEventResult) OVERRIDE;
virtual void didHandleFetchEvent(int fetchEventID, PassRefPtr<WebCore::Response>) OVERRIDE;
virtual void didHandleSyncEvent(int syncEventID) OVERRIDE;
virtual void postMessageToClient(int clientID, const WebString& message, PassOwnPtr<WebMessagePortChannelArray>) OVERRIDE;
virtual void trace(WebCore::Visitor* visitor) OVERRIDE { WebCore::ServiceWorkerGlobalScopeClient::trace(visitor); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment