diff options
38 files changed, 1066 insertions, 133 deletions
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index 6d0430c..4018c9f 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp @@ -120,6 +120,7 @@ '../third_party/WebKit/public/blink.gyp:blink', '../ui/gl/gl.gyp:gl', '../ui/shell_dialogs/shell_dialogs.gyp:shell_dialogs', + '../v8/tools/gyp/v8.gyp:v8', '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu', 'android_webview_pak', 'android_webview_version', @@ -156,6 +157,9 @@ 'browser/aw_javascript_dialog_manager.h', 'browser/aw_login_delegate.cc', 'browser/aw_login_delegate.h', + 'browser/aw_message_port_message_filter.cc', + 'browser/aw_message_port_message_filter.h', + 'browser/aw_message_port_service.h', 'browser/aw_pref_store.cc', 'browser/aw_pref_store.h', 'browser/aw_printing_message_filter.cc', @@ -226,6 +230,7 @@ 'common/aw_crash_handler.h', 'common/aw_hit_test_data.cc', 'common/aw_hit_test_data.h', + 'common/aw_message_port_messages.h', 'common/aw_resource.h', 'common/aw_switches.cc', 'common/aw_switches.h', @@ -245,6 +250,8 @@ 'renderer/aw_content_renderer_client.h', 'renderer/aw_key_systems.cc', 'renderer/aw_key_systems.h', + 'renderer/aw_message_port_client.cc', + 'renderer/aw_message_port_client.h', 'renderer/aw_permission_client.cc', 'renderer/aw_permission_client.h', 'renderer/aw_render_process_observer.cc', diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index 1fea722..cd620c6 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc @@ -237,6 +237,14 @@ AwURLRequestContextGetter* AwBrowserContext::GetAwURLRequestContext() { return url_request_context_getter_.get(); } +AwMessagePortService* AwBrowserContext::GetMessagePortService() { + if (!message_port_service_.get()) { + message_port_service_.reset( + native_factory_->CreateAwMessagePortService()); + } + return message_port_service_.get(); +} + // Create user pref service for autofill functionality. void AwBrowserContext::CreateUserPrefServiceIfNecessary() { if (user_pref_service_) diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h index a821acf..d6e44b3 100644 --- a/android_webview/browser/aw_browser_context.h +++ b/android_webview/browser/aw_browser_context.h @@ -8,6 +8,7 @@ #include <vector> #include "android_webview/browser/aw_download_manager_delegate.h" +#include "android_webview/browser/aw_message_port_service.h" #include "android_webview/browser/aw_ssl_host_state_delegate.h" #include "base/basictypes.h" #include "base/compiler_specific.h" @@ -101,6 +102,8 @@ class AwBrowserContext : public content::BrowserContext, void CreateUserPrefServiceIfNecessary(); + AwMessagePortService* GetMessagePortService(); + // content::BrowserContext implementation. scoped_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate( const base::FilePath& partition_path) override; @@ -143,6 +146,7 @@ class AwBrowserContext : public content::BrowserContext, scoped_refptr<AwURLRequestContextGetter> url_request_context_getter_; scoped_refptr<AwQuotaManagerBridge> quota_manager_bridge_; scoped_ptr<AwFormDatabaseService> form_database_service_; + scoped_ptr<AwMessagePortService> message_port_service_; AwDownloadManagerDelegate download_manager_delegate_; diff --git a/android_webview/browser/aw_message_port_message_filter.cc b/android_webview/browser/aw_message_port_message_filter.cc new file mode 100644 index 0000000..74b1aa5 --- /dev/null +++ b/android_webview/browser/aw_message_port_message_filter.cc @@ -0,0 +1,60 @@ +// Copyright 2015 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 "android_webview/browser/aw_message_port_message_filter.h" + +#include "android_webview/browser/aw_browser_context.h" +#include "android_webview/common/aw_message_port_messages.h" +#include "content/public/browser/message_port_provider.h" + +using content::BrowserThread; +using content::MessagePortProvider; + +namespace android_webview { + +AwMessagePortMessageFilter::AwMessagePortMessageFilter(int route_id) + : BrowserMessageFilter(AwMessagePortMsgStart), route_id_(route_id) { +} + +AwMessagePortMessageFilter::~AwMessagePortMessageFilter() { +} + +void AwMessagePortMessageFilter::OnChannelClosing() { + MessagePortProvider::OnMessagePortDelegateClosing(this); + AwBrowserContext::GetDefault()->GetMessagePortService()-> + OnMessagePortMessageFilterClosing(this); +} + +bool AwMessagePortMessageFilter::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AwMessagePortMessageFilter, message) + IPC_MESSAGE_FORWARD(AwMessagePortHostMsg_ConvertedMessage, + AwBrowserContext::GetDefault()->GetMessagePortService(), + AwMessagePortService::OnConvertedMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void AwMessagePortMessageFilter::OnDestruct() const { + BrowserThread::DeleteOnIOThread::Destruct(this); +} + +void AwMessagePortMessageFilter::SendMessage( + int msg_port_route_id, + const base::string16& message, + const std::vector<int>& sent_message_port_ids) { + Send(new AwMessagePortMsg_Message(route_id_, + msg_port_route_id, // same as the port id + message, + sent_message_port_ids)); +} + +void AwMessagePortMessageFilter::SendMessagesAreQueued(int route_id) { + // TODO(sgurun) implement + NOTREACHED(); +} + +} // namespace android_webview diff --git a/android_webview/browser/aw_message_port_message_filter.h b/android_webview/browser/aw_message_port_message_filter.h new file mode 100644 index 0000000..d63774b --- /dev/null +++ b/android_webview/browser/aw_message_port_message_filter.h @@ -0,0 +1,45 @@ +// Copyright 2015 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. + +#ifndef ANDROID_WEBVIEW_BROWSER_MESSAGE_PORT_MESSAGE_FILTER_H_ +#define ANDROID_WEBVIEW_BROWSER_MESSAGE_PORT_MESSAGE_FILTER_H_ + +#include "base/callback.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/message_port_delegate.h" + +namespace android_webview { + +// Filter for Aw specific MessagePort related IPC messages (creating and +// destroying a MessagePort, sending a message via a MessagePort etc). +class AwMessagePortMessageFilter : public content::BrowserMessageFilter, + public content::MessagePortDelegate { + public: + explicit AwMessagePortMessageFilter(int route_id); + + // BrowserMessageFilter implementation. + void OnChannelClosing() override; + bool OnMessageReceived(const IPC::Message& message) override; + void OnDestruct() const override; + + // MessagePortDelegate implementation. + void SendMessage(int msg_port_route_id, + const base::string16& message, + const std::vector<int>& sent_message_port_ids) override; + void SendMessagesAreQueued(int route_id) override; + + private: + friend class content::BrowserThread; + friend class base::DeleteHelper<AwMessagePortMessageFilter>; + + ~AwMessagePortMessageFilter() override; + + int route_id_; + + DISALLOW_COPY_AND_ASSIGN(AwMessagePortMessageFilter); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_MESSAGE_PORT_MESSAGE_FILTER_H_ diff --git a/android_webview/browser/aw_message_port_service.h b/android_webview/browser/aw_message_port_service.h new file mode 100644 index 0000000..086480e --- /dev/null +++ b/android_webview/browser/aw_message_port_service.h @@ -0,0 +1,32 @@ +// Copyright 2015 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. + +#ifndef ANDROID_WEBVIEW_BROWSER_AW_MESSAGE_PORT_SERVICE_H_ +#define ANDROID_WEBVIEW_BROWSER_AW_MESSAGE_PORT_SERVICE_H_ + +#include <vector> + +#include "base/values.h" + +namespace android_webview { + +class AwMessagePortMessageFilter; + +// The interface for AwMessagePortService +class AwMessagePortService { + public: + virtual ~AwMessagePortService() { } + + virtual void OnConvertedMessage( + int message_port_id, + const base::ListValue& message, + const std::vector<int>& sent_message_port_ids) = 0; + + virtual void OnMessagePortMessageFilterClosing( + AwMessagePortMessageFilter* filter) = 0; +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_AW_MESSAGE_PORT_SERVICE_H_ diff --git a/android_webview/browser/jni_dependency_factory.h b/android_webview/browser/jni_dependency_factory.h index 983ef2a..d27e9b2 100644 --- a/android_webview/browser/jni_dependency_factory.h +++ b/android_webview/browser/jni_dependency_factory.h @@ -16,6 +16,7 @@ class WebContentsViewDelegate; namespace android_webview { class AwBrowserContext; +class AwMessagePortService; class AwQuotaManagerBridge; class AwWebPreferencesPopulater; @@ -29,6 +30,7 @@ class JniDependencyFactory { virtual content::WebContentsViewDelegate* CreateViewDelegate( content::WebContents* web_contents) = 0; virtual AwWebPreferencesPopulater* CreateWebPreferencesPopulater() = 0; + virtual AwMessagePortService* CreateAwMessagePortService() = 0; #if defined(VIDEO_HOLE) virtual content::ExternalVideoSurfaceContainer* CreateExternalVideoSurfaceContainer(content::WebContents* contents) = 0; diff --git a/android_webview/common/android_webview_message_generator.h b/android_webview/common/android_webview_message_generator.h index 1ac28dc..f5dcce8 100644 --- a/android_webview/common/android_webview_message_generator.h +++ b/android_webview/common/android_webview_message_generator.h @@ -4,5 +4,6 @@ // Multiply-included file, hence no include guard. +#include "android_webview/common/aw_message_port_messages.h" #include "android_webview/common/print_messages.h" #include "android_webview/common/render_view_messages.h" diff --git a/android_webview/common/aw_message_port_messages.h b/android_webview/common/aw_message_port_messages.h new file mode 100644 index 0000000..866d713 --- /dev/null +++ b/android_webview/common/aw_message_port_messages.h @@ -0,0 +1,32 @@ +// Copyright 2015 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. + +// Multiply-included file, no traditional include guard. +#include <vector> + +#include "base/basictypes.h" +#include "ipc/ipc_message_macros.h" + +#define IPC_MESSAGE_START AwMessagePortMsgStart + +//----------------------------------------------------------------------------- +// MessagePort messages +// These are messages sent from the browser to the renderer process. + +// Tells the renderer to convert the sent message from a WebSerializeScript +// format to a base::ListValue. Due to the complexities of renderer/browser +// relation, this can only be done in renderer for now. +IPC_MESSAGE_ROUTED3(AwMessagePortMsg_Message, + int /* recipient message port id */, + base::string16 /* message */, + std::vector<int> /* sent message port_ids */) + +//----------------------------------------------------------------------------- +// These are messages sent from the renderer to the browser process. + +// Response to AwMessagePortMessage_ConvertMessage +IPC_MESSAGE_ROUTED3(AwMessagePortHostMsg_ConvertedMessage, + int /* recipient message port id */, + base::ListValue /* converted message */, + std::vector<int> /* sent message port_ids */) diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java index 77ad0ba..36463ed 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java +++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java @@ -28,6 +28,7 @@ public class AwBrowserContext { private AwFormDatabase mFormDatabase; private HttpAuthDatabase mHttpAuthDatabase; private DefaultAndroidKeyStore mLocalKeyStore; + private AwMessagePortService mMessagePortService; public AwBrowserContext(SharedPreferences sharedPreferences) { mSharedPreferences = sharedPreferences; @@ -68,6 +69,13 @@ public class AwBrowserContext { return mLocalKeyStore; } + public AwMessagePortService createMessagePortService() { + if (mMessagePortService == null) { + mMessagePortService = new AwMessagePortService(); + } + return mMessagePortService; + } + /** * @see android.webkit.WebView#pauseTimers() */ diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 4d80be7..7039351 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -1790,9 +1790,16 @@ public class AwContents implements SmartClipProvider { * message ports to pass. */ public void postMessageToFrame(String frameName, String message, - String sourceOrigin, String targetOrigin, int[] msgPorts) { + String sourceOrigin, String targetOrigin, MessagePort[] msgPorts) { + if (isDestroyed()) return; + int[] portIds = null; + if (msgPorts != null) { + portIds = new int[msgPorts.length]; + for (int i = 0; i < msgPorts.length; i++) + portIds[i] = msgPorts[i].portId(); + } nativePostMessageToFrame(mNativeAwContents, frameName, message, sourceOrigin, - targetOrigin, msgPorts); + targetOrigin, portIds); } /** @@ -1801,10 +1808,12 @@ public class AwContents implements SmartClipProvider { * @param callback The message channel created. */ public void createMessageChannel(ValueCallback<MessageChannel> callback) { + if (isDestroyed()) return; + // Make sure the message port service is created. + mBrowserContext.createMessagePortService(); nativeCreateMessageChannel(mNativeAwContents, callback); } - //-------------------------------------------------------------------------------------------- // View and ViewGroup method implementations //-------------------------------------------------------------------------------------------- @@ -2217,12 +2226,6 @@ public class AwContents implements SmartClipProvider { } } - @CalledByNative - private static void onMessageChannelCreated(int portId1, int portId2, - ValueCallback<MessageChannel> callback) { - callback.onReceiveValue(new MessageChannel(portId1, portId2)); - } - // ------------------------------------------------------------------------------------------- // Helper methods // ------------------------------------------------------------------------------------------- diff --git a/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java b/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java new file mode 100644 index 0000000..df9e3b7 --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java @@ -0,0 +1,136 @@ +// Copyright 2015 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. + +package org.chromium.android_webview; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.SparseArray; +import android.webkit.ValueCallback; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Provides the Message Channel functionality for Android Webview. Specifically + * manages the message ports that are associated with a message channel and + * handles posting/receiving messages to/from them. + * See https://html.spec.whatwg.org/multipage/comms.html#messagechannel for + * further information on message channels. + * + * The message ports have unique IDs. In Android webview implementation, + * the message ports are only known by their IDs at the native side. + * At the java side, the embedder deals with MessagePort objects. The mapping + * from an ID to an object is in AwMessagePortService. AwMessagePortService + * keeps a strong ref to MessagePort objects until they are closed. + * + * Ownership: The Java AwMessagePortService is owned by Java AwBrowserContext. + * The native AwMessagePortService is owned by native AwBrowserContext. The + * native peer maintains a weak ref to the java object and deregisters itself + * before being deleted. + * + * All methods are called on UI thread except as noted. + */ +@JNINamespace("android_webview") +public class AwMessagePortService { + + private static final String TAG = "AwMessagePortService"; + + private static final int POST_MESSAGE = 1; + + // TODO(sgurun) implement transferring ports from JS to Java using message channels. + private static class PostMessage { + public MessagePort port; + public String message; + + public PostMessage(MessagePort port, String message) { + this.port = port; + this.message = message; + } + } + + // The messages from JS to Java are posted to a message port on a background thread. + // We do this to make any potential synchronization between Java and JS + // easier for user programs. + private static class MessageHandler extends Thread { + private Handler mHandler; + + public Handler getHandler() { + return mHandler; + } + + public void run() { + Looper.prepare(); + mHandler = new Handler() { + public void handleMessage(Message msg) { + if (msg.what == POST_MESSAGE) { + PostMessage m = (PostMessage) msg.obj; + m.port.onMessage(m.message); + return; + } + throw new IllegalStateException("undefined message"); + } + }; + Looper.loop(); + } + } + + // A thread safe storage for Message Ports. + private static class MessagePortStorage { + private SparseArray<MessagePort> mMessagePorts = new SparseArray<MessagePort>(); + private Object mLock = new Object(); + + public void put(int portId, MessagePort m) { + synchronized (mLock) { + mMessagePorts.put(portId, m); + } + } + public MessagePort get(int portId) { + synchronized (mLock) { + return mMessagePorts.get(portId); + } + } + } + + private long mNativeMessagePortService; + private MessagePortStorage mPortStorage = new MessagePortStorage(); + private MessageHandler mMessageHandler = new MessageHandler(); + + AwMessagePortService() { + mNativeMessagePortService = nativeInitAwMessagePortService(); + mMessageHandler.start(); + } + + private MessagePort addPort(int portId) { + if (mPortStorage.get(portId) != null) { + throw new IllegalStateException("Port already exists"); + } + MessagePort m = new MessagePort(portId); + mPortStorage.put(portId, m); + return m; + } + + @CalledByNative + private void onMessageChannelCreated(int portId1, int portId2, + ValueCallback<MessageChannel> callback) { + callback.onReceiveValue(new MessageChannel(addPort(portId1), addPort(portId2))); + } + + // Called on IO thread. + @CalledByNative + private void onPostMessage(int portId, String message, int[] ports) { + PostMessage m = new PostMessage(mPortStorage.get(portId), message); + Handler handler = mMessageHandler.getHandler(); + Message msg = handler.obtainMessage(POST_MESSAGE, m); + handler.sendMessage(msg); + } + + @CalledByNative + private void unregisterNativeAwMessagePortService() { + mNativeMessagePortService = 0; + } + + private native long nativeInitAwMessagePortService(); +} diff --git a/android_webview/java/src/org/chromium/android_webview/MessageChannel.java b/android_webview/java/src/org/chromium/android_webview/MessageChannel.java index 9248624..d2957ce 100644 --- a/android_webview/java/src/org/chromium/android_webview/MessageChannel.java +++ b/android_webview/java/src/org/chromium/android_webview/MessageChannel.java @@ -9,20 +9,20 @@ package org.chromium.android_webview; * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#message-channels */ public class MessageChannel { - // The message port IDs of port1 and port2. - private int mPort1; - private int mPort2; + // The message ports of MessageChannel. + private MessagePort mPort1; + private MessagePort mPort2; - public MessageChannel(int port1, int port2) { + public MessageChannel(MessagePort port1, MessagePort port2) { mPort1 = port1; mPort2 = port2; } - public int port1() { + public MessagePort port1() { return mPort1; } - public int port2() { + public MessagePort port2() { return mPort2; } } diff --git a/android_webview/java/src/org/chromium/android_webview/MessagePort.java b/android_webview/java/src/org/chromium/android_webview/MessagePort.java new file mode 100644 index 0000000..ef0e8d7 --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/MessagePort.java @@ -0,0 +1,46 @@ +// Copyright 2015 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. + +package org.chromium.android_webview; + +import android.util.Log; + +/** + * Represents the MessageChannel MessagePort object. Inspired from + * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#message-channels + */ +public class MessagePort { + + /** + * The interface for message handler for receiving messages. Called on a background thread. + */ + public static interface MessageHandler { + void onMessage(String message); + }; + + private static final String TAG = "AwMessagePortService"; + + private int mPortId; + private MessageHandler mHandler; + + public MessagePort(int portId) { + mPortId = portId; + } + + public int portId() { + return mPortId; + } + + public void setMessageHandler(MessageHandler handler) { + mHandler = handler; + } + + public void onMessage(String message) { + if (mHandler == null) { + Log.w(TAG, "No handler set for port [" + mPortId + "], dropping message " + message); + return; + } + mHandler.onMessage(message); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java index 7d7ea86..3be279c9 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java @@ -13,6 +13,7 @@ import static org.chromium.content.browser.test.util.TestCallbackHelperContainer import org.chromium.android_webview.AwContents; import org.chromium.android_webview.MessageChannel; +import org.chromium.android_webview.MessagePort; import org.chromium.android_webview.test.util.CommonResources; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.MinAndroidSdkLevel; @@ -25,12 +26,11 @@ import org.chromium.net.test.util.TestWebServer; public class PostMessageTest extends AwTestBase { private static final String SOURCE_ORIGIN = "android_webview"; + // Timeout to failure, in milliseconds + private static final long TIMEOUT = scaleTimeout(5000); // Inject to the page to verify received messages. private static class MessageObject { - // Timeout to failure, in milliseconds - private static final long TIMEOUT = scaleTimeout(5000); - private boolean mReady; private String mData; private String mOrigin; @@ -102,12 +102,16 @@ public class PostMessageTest extends AwTestBase { } private static final String WEBVIEW_MESSAGE = "from_webview"; + private static final String JS_MESSAGE = "from_js"; private static final String TEST_PAGE = "<!DOCTYPE html><html><body>" + " <script type=\"text/javascript\">" + " onmessage = function (e) {" + " messageObject.setMessageParams(e.data, e.origin, e.ports);" + + " if (e.ports != null && e.ports.length > 0) {" + + " e.ports[0].postMessage(\"" + JS_MESSAGE + "\");" + + " }" + " }" + " </script>" + "</body></html>"; @@ -137,22 +141,61 @@ public class PostMessageTest extends AwTestBase { assertEquals(SOURCE_ORIGIN, mMessageObject.getOrigin()); } - // TODO(sgurun) This test verifies a channel is created by posting one of the - // ports of the channel to a MessagePort and verifying one port is received. - // in a next CL we will update the JS to post messages back to Webview so - // we could do a more thorough verification. + private static class ChannelContainer { + private boolean mReady; + private MessageChannel mChannel; + private Object mLock = new Object(); + private String mMessage; + + public void set(MessageChannel channel) { + mChannel = channel; + } + public MessageChannel get() { + return mChannel; + } + + public void setMessage(String message) { + synchronized (mLock) { + mMessage = message; + mReady = true; + mLock.notify(); + } + } + + public String getMessage() { + return mMessage; + } + + public void waitForMessage() throws InterruptedException { + synchronized (mLock) { + if (!mReady) mLock.wait(TIMEOUT); + } + } + } + + // Verify that a channel can be created and basic full duplex communication + // can happen on it. @SmallTest @Feature({"AndroidWebView", "Android-PostMessage"}) public void testCreateChannel() throws Throwable { loadPage(TEST_PAGE); + final ChannelContainer channelContainer = new ChannelContainer(); runTestOnUiThread(new Runnable() { @Override public void run() { ValueCallback<MessageChannel> callback = new ValueCallback<MessageChannel>() { @Override public void onReceiveValue(MessageChannel channel) { + // verify communication from JS to Java. + channelContainer.set(channel); + channel.port1().setMessageHandler(new MessagePort.MessageHandler() { + @Override + public void onMessage(String message) { + channelContainer.setMessage(message); + } + }); mAwContents.postMessageToFrame(null, WEBVIEW_MESSAGE, SOURCE_ORIGIN, - mWebServer.getBaseUrl(), new int[]{channel.port2()}); + mWebServer.getBaseUrl(), new MessagePort[]{channel.port2()}); } }; mAwContents.createMessageChannel(callback); @@ -161,8 +204,21 @@ public class PostMessageTest extends AwTestBase { mMessageObject.waitForMessage(); assertEquals(WEBVIEW_MESSAGE, mMessageObject.getData()); assertEquals(SOURCE_ORIGIN, mMessageObject.getOrigin()); - // verify that one message port is received. + // verify that one message port is received at the js side assertEquals(1, mMessageObject.getPorts().length); + // wait until we receive a message from JS + runTestOnUiThread(new Runnable() { + @Override + public void run() { + try { + channelContainer.waitForMessage(); + } catch (InterruptedException e) { + // ignore. + } + } + }); + assertEquals(JS_MESSAGE, channelContainer.getMessage()); + // TODO(sgurun) verify communication from Java to JS on the created channel } private static final String WORKER_MESSAGE = "from_worker"; @@ -211,7 +267,7 @@ public class PostMessageTest extends AwTestBase { public void onReceiveValue(MessageChannel channel) { mAwContents.postMessageToFrame(null, WEBVIEW_MESSAGE, SOURCE_ORIGIN, mWebServer.getBaseUrl(), - new int[]{channel.port1(), channel.port2()}); + new MessagePort[]{channel.port1(), channel.port2()}); } }; mAwContents.createMessageChannel(callback); diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc index 0f89e85..8ad7657 100644 --- a/android_webview/lib/main/aw_main_delegate.cc +++ b/android_webview/lib/main/aw_main_delegate.cc @@ -10,6 +10,7 @@ #include "android_webview/lib/aw_browser_dependency_factory_impl.h" #include "android_webview/native/aw_assets.h" #include "android_webview/native/aw_media_url_interceptor.h" +#include "android_webview/native/aw_message_port_service_impl.h" #include "android_webview/native/aw_quota_manager_bridge_impl.h" #include "android_webview/native/aw_web_contents_view_delegate.h" #include "android_webview/native/aw_web_preferences_populater_impl.h" @@ -171,6 +172,10 @@ AwWebPreferencesPopulater* AwMainDelegate::CreateWebPreferencesPopulater() { return new AwWebPreferencesPopulaterImpl(); } +AwMessagePortService* AwMainDelegate::CreateAwMessagePortService() { + return new AwMessagePortServiceImpl(); +} + #if defined(VIDEO_HOLE) content::ExternalVideoSurfaceContainer* AwMainDelegate::CreateExternalVideoSurfaceContainer( diff --git a/android_webview/lib/main/aw_main_delegate.h b/android_webview/lib/main/aw_main_delegate.h index ebf25fc..167d921 100644 --- a/android_webview/lib/main/aw_main_delegate.h +++ b/android_webview/lib/main/aw_main_delegate.h @@ -30,23 +30,23 @@ class AwMainDelegate : public content::ContentMainDelegate, private: // content::ContentMainDelegate implementation: - virtual bool BasicStartupComplete(int* exit_code) override; - virtual void PreSandboxStartup() override; - virtual void SandboxInitialized(const std::string& process_type) override; - virtual int RunProcess( + bool BasicStartupComplete(int* exit_code) override; + void PreSandboxStartup() override; + void SandboxInitialized(const std::string& process_type) override; + int RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) override; - virtual void ProcessExiting(const std::string& process_type) override; - virtual content::ContentBrowserClient* CreateContentBrowserClient() override; - virtual content::ContentRendererClient* - CreateContentRendererClient() override; + void ProcessExiting(const std::string& process_type) override; + content::ContentBrowserClient* CreateContentBrowserClient() override; + content::ContentRendererClient* CreateContentRendererClient() override; // JniDependencyFactory implementation. - virtual scoped_refptr<AwQuotaManagerBridge> CreateAwQuotaManagerBridge( + scoped_refptr<AwQuotaManagerBridge> CreateAwQuotaManagerBridge( AwBrowserContext* browser_context) override; - virtual content::WebContentsViewDelegate* CreateViewDelegate( + content::WebContentsViewDelegate* CreateViewDelegate( content::WebContents* web_contents) override; - virtual AwWebPreferencesPopulater* CreateWebPreferencesPopulater() override; + AwWebPreferencesPopulater* CreateWebPreferencesPopulater() override; + AwMessagePortService* CreateAwMessagePortService() override; #if defined(VIDEO_HOLE) virtual content::ExternalVideoSurfaceContainer* CreateExternalVideoSurfaceContainer( diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc index 253a070..e81853f 100644 --- a/android_webview/native/android_webview_jni_registrar.cc +++ b/android_webview/native/android_webview_jni_registrar.cc @@ -14,6 +14,7 @@ #include "android_webview/native/aw_dev_tools_server.h" #include "android_webview/native/aw_form_database.h" #include "android_webview/native/aw_http_auth_handler.h" +#include "android_webview/native/aw_message_port_service_impl.h" #include "android_webview/native/aw_pdf_exporter.h" #include "android_webview/native/aw_picture.h" #include "android_webview/native/aw_quota_manager_bridge_impl.h" @@ -58,6 +59,7 @@ static base::android::RegistrationMethod kWebViewRegisteredMethods[] = { { "AwWebResourceResponseImpl", RegisterAwWebResourceResponse }, { "InputStream", RegisterInputStream }, { "JavaBrowserViewRendererHelper", RegisterJavaBrowserViewRendererHelper }, + { "AwMessagePortService", RegisterAwMessagePortService }, }; bool RegisterJni(JNIEnv* env) { diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index 2a9a35f..64cd9a9 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -21,6 +21,7 @@ #include "android_webview/native/aw_browser_dependency_factory.h" #include "android_webview/native/aw_contents_client_bridge.h" #include "android_webview/native/aw_contents_io_thread_client_impl.h" +#include "android_webview/native/aw_message_port_service_impl.h" #include "android_webview/native/aw_pdf_exporter.h" #include "android_webview/native/aw_picture.h" #include "android_webview/native/aw_web_contents_delegate.h" @@ -133,23 +134,6 @@ void OnIoThreadClientReady(content::RenderFrameHost* rfh) { render_process_id, render_frame_id); } -void OnMessageChannelCreated(ScopedJavaGlobalRef<jobject>* callback, - int* port1, - int* port2) { - JNIEnv* env = AttachCurrentThread(); - Java_AwContents_onMessageChannelCreated(env, *port1, *port2, - callback->obj()); -} - -void PostMessageToFrameOnIOThread(WebContents* web_contents, - base::string16* source_origin, - base::string16* target_origin, - base::string16* data, - std::vector<int>* ports) { - content::MessagePortProvider::PostMessageToFrame(web_contents, - *source_origin, *target_origin, *data, *ports); -} - } // namespace // static @@ -1084,49 +1068,40 @@ void AwContents::PostMessageToFrame(JNIEnv* env, jobject obj, jstring frame_name, jstring message, jstring source_origin, jstring target_origin, jintArray msgPorts) { - base::string16* j_source_origin = new base::string16; - ConvertJavaStringToUTF16(env, source_origin, j_source_origin); - base::string16* j_target_origin = new base::string16; - ConvertJavaStringToUTF16(env, target_origin, j_target_origin); - base::string16* j_message = new base::string16; - ConvertJavaStringToUTF16(env, message, j_message); - std::vector<int>* j_ports = new std::vector<int>; - + base::string16 j_source_origin(ConvertJavaStringToUTF16(env, source_origin)); + base::string16 j_target_origin(ConvertJavaStringToUTF16(env, target_origin)); + base::string16 j_message(ConvertJavaStringToUTF16(env, message)); + std::vector<int> j_ports; if (msgPorts != nullptr) - base::android::JavaIntArrayToIntVector(env, msgPorts, j_ports); - - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&PostMessageToFrameOnIOThread, - web_contents_.get(), - base::Owned(j_source_origin), - base::Owned(j_target_origin), - base::Owned(j_message), - base::Owned(j_ports))); + base::android::JavaIntArrayToIntVector(env, msgPorts, &j_ports); + + content::MessagePortProvider::PostMessageToFrame(web_contents_.get(), + j_source_origin, + j_target_origin, + j_message, + j_ports); +} + +scoped_refptr<AwMessagePortMessageFilter> +AwContents::GetMessagePortMessageFilter() { + // Create a message port message filter if necessary + if (message_port_message_filter_.get() == nullptr) { + message_port_message_filter_ = + new AwMessagePortMessageFilter( + web_contents_->GetMainFrame()->GetRoutingID()); + web_contents_->GetRenderProcessHost()->AddFilter( + message_port_message_filter_.get()); + } + return message_port_message_filter_; } void AwContents::CreateMessageChannel(JNIEnv* env, jobject obj, jobject callback) { - ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>(); - j_callback->Reset(env, callback); - int* port1 = new int; - int* port2 = new int; - BrowserThread::PostTaskAndReply( - BrowserThread::IO, - FROM_HERE, - base::Bind(&content::MessagePortProvider::CreateMessageChannel, - web_contents_.get(), - port1, - port2), - base::Bind(&OnMessageChannelCreated, - base::Owned(j_callback), - base::Owned(port1), - base::Owned(port2))); + AwMessagePortServiceImpl::GetInstance()->CreateMessageChannel(env, callback, + GetMessagePortMessageFilter()); } - void SetShouldDownloadFavicons(JNIEnv* env, jclass jclazz) { g_should_download_favicons = true; } diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 4a4afbb..13ad67c 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -11,6 +11,7 @@ #include <utility> #include "android_webview/browser/aw_browser_permission_request_delegate.h" +#include "android_webview/browser/aw_message_port_message_filter.h" #include "android_webview/browser/browser_view_renderer.h" #include "android_webview/browser/browser_view_renderer_client.h" #include "android_webview/browser/find_helper.h" @@ -220,6 +221,7 @@ class AwContents : public FindHelper::Listener, void SetJsOnlineProperty(JNIEnv* env, jobject obj, jboolean network_up); void TrimMemory(JNIEnv* env, jobject obj, jint level, jboolean visible); + scoped_refptr<AwMessagePortMessageFilter> GetMessagePortMessageFilter(); void PostMessageToFrame(JNIEnv* env, jobject obj, jstring frame_id, jstring message, jstring source_origin, jstring target_origin, jintArray msgPorts); @@ -244,6 +246,7 @@ class AwContents : public FindHelper::Listener, BrowserViewRenderer browser_view_renderer_; scoped_ptr<AwPdfExporter> pdf_exporter_; scoped_ptr<PermissionRequestHandler> permission_request_handler_; + scoped_refptr<AwMessagePortMessageFilter> message_port_message_filter_; // GURL is supplied by the content layer as requesting frame. // Callback is supplied by the content layer, and is invoked with the result diff --git a/android_webview/native/aw_message_port_service_impl.cc b/android_webview/native/aw_message_port_service_impl.cc new file mode 100644 index 0000000..5fa4e1b --- /dev/null +++ b/android_webview/native/aw_message_port_service_impl.cc @@ -0,0 +1,161 @@ +// Copyright 2015 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 "android_webview/native/aw_message_port_service_impl.h" + +#include "android_webview/browser/aw_browser_context.h" +#include "android_webview/browser/aw_message_port_message_filter.h" +#include "android_webview/native/aw_contents.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/bind.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/message_port_provider.h" +#include "jni/AwMessagePortService_jni.h" + +namespace android_webview { + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF16ToJavaString; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaIntArray; +using content::BrowserThread; +using content::MessagePortProvider; + +//static +AwMessagePortServiceImpl* AwMessagePortServiceImpl::GetInstance() { + return static_cast<AwMessagePortServiceImpl*>( + AwBrowserContext::GetDefault()->GetMessagePortService()); +} + +AwMessagePortServiceImpl::AwMessagePortServiceImpl() { +} + +AwMessagePortServiceImpl::~AwMessagePortServiceImpl() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + Java_AwMessagePortService_unregisterNativeAwMessagePortService(env, + obj.obj()); +} + +void AwMessagePortServiceImpl::Init(JNIEnv* env, jobject obj) { + java_ref_ = JavaObjectWeakGlobalRef(env, obj); +} + +void AwMessagePortServiceImpl::CreateMessageChannel( + JNIEnv* env, + jobject callback, + scoped_refptr<AwMessagePortMessageFilter> filter) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>(); + j_callback->Reset(env, callback); + + int* portId1 = new int; + int* portId2 = new int; + BrowserThread::PostTaskAndReply( + BrowserThread::IO, + FROM_HERE, + base::Bind(&AwMessagePortServiceImpl::CreateMessageChannelOnIOThread, + base::Unretained(this), + filter, + portId1, + portId2), + base::Bind(&AwMessagePortServiceImpl::OnMessageChannelCreated, + base::Unretained(this), + base::Owned(j_callback), + base::Owned(portId1), + base::Owned(portId2))); +} + +void AwMessagePortServiceImpl::OnConvertedMessage( + int message_port_id, + const base::ListValue& message, + const std::vector<int>& sent_message_port_ids) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> jobj = java_ref_.get(env); + if (jobj.is_null()) + return; + + if (message.GetSize() != 1) { + NOTREACHED(); + return; + } + + base::string16 value; + if (!message.GetString(0, &value)) { + LOG(WARNING) << "Converting post message to a string failed for port " + << message_port_id; + return; + } + ScopedJavaLocalRef<jstring> jmsg = ConvertUTF16ToJavaString(env, value); + ScopedJavaLocalRef<jintArray> jports = + ToJavaIntArray(env, sent_message_port_ids); + Java_AwMessagePortService_onPostMessage(env, + jobj.obj(), + message_port_id, + jmsg.obj(), + jports.obj()); +} + +void AwMessagePortServiceImpl::OnMessagePortMessageFilterClosing( + AwMessagePortMessageFilter* filter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + for (MessagePorts::iterator iter = ports_.begin(); + iter != ports_.end(); iter++) { + if (iter->second == filter) { + ports_.erase(iter); + } + } +} + +void AwMessagePortServiceImpl::CreateMessageChannelOnIOThread( + scoped_refptr<AwMessagePortMessageFilter> filter, + int* portId1, + int* portId2) { + content::MessagePortProvider::CreateMessageChannel(filter.get(), portId1, + portId2); + AddPort(*portId1, filter.get()); + AddPort(*portId2, filter.get()); +} + +void AwMessagePortServiceImpl::OnMessageChannelCreated( + ScopedJavaGlobalRef<jobject>* callback, + int* port1, + int* port2) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + Java_AwMessagePortService_onMessageChannelCreated(env, obj.obj(), *port1, + *port2, callback->obj()); +} + +void AwMessagePortServiceImpl::AddPort(int message_port_id, + AwMessagePortMessageFilter* filter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (ports_.count(message_port_id)) { + NOTREACHED(); + return; + } + ports_[message_port_id] = filter; +} + +bool RegisterAwMessagePortService(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// static +jlong InitAwMessagePortService(JNIEnv* env, jobject obj) { + AwMessagePortServiceImpl* service = AwMessagePortServiceImpl::GetInstance(); + service->Init(env, obj); + return reinterpret_cast<intptr_t>(service); +} + +} // namespace android_webview diff --git a/android_webview/native/aw_message_port_service_impl.h b/android_webview/native/aw_message_port_service_impl.h new file mode 100644 index 0000000..d938b6e --- /dev/null +++ b/android_webview/native/aw_message_port_service_impl.h @@ -0,0 +1,65 @@ +// Copyright 2015 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. + +#ifndef ANDROID_WEBVIEW_NATIVE_AW_MESSAGE_PORT_SERVICE_IMPL_H_ +#define ANDROID_WEBVIEW_NATIVE_AW_MESSAGE_PORT_SERVICE_IMPL_H_ + +#include <jni.h> +#include <map> + +#include "android_webview/browser/aw_message_port_service.h" +#include "base/android/jni_weak_ref.h" +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" + +namespace android_webview { + +// This class is the native peer of AwMessagePortService.java. Please see the +// java class for an explanation of use, ownership and lifetime. + +// Threading: Created and initialized on UI thread. For other methods, see +// the method level DCHECKS or documentation. +class AwMessagePortServiceImpl : public AwMessagePortService { + public: + static AwMessagePortServiceImpl* GetInstance(); + + AwMessagePortServiceImpl(); + ~AwMessagePortServiceImpl(); + void Init(JNIEnv* env, jobject object); + + void CreateMessageChannel(JNIEnv* env, jobject callback, + scoped_refptr<AwMessagePortMessageFilter> filter); + + // AwMessagePortService implementation + void OnConvertedMessage( + int message_port_id, + const base::ListValue& message, + const std::vector<int>& sent_message_port_ids) override; + void OnMessagePortMessageFilterClosing( + AwMessagePortMessageFilter* filter) override; + +private: + void CreateMessageChannelOnIOThread( + scoped_refptr<AwMessagePortMessageFilter> filter, + int* port1, + int* port2); + void OnMessageChannelCreated( + base::android::ScopedJavaGlobalRef<jobject>* callback, + int* port1, + int* port2); + void AddPort(int message_port_id, AwMessagePortMessageFilter* filter); + + JavaObjectWeakGlobalRef java_ref_; + typedef std::map<int, AwMessagePortMessageFilter*> MessagePorts; + MessagePorts ports_; // Access on IO thread + + DISALLOW_COPY_AND_ASSIGN(AwMessagePortServiceImpl); +}; + +bool RegisterAwMessagePortService(JNIEnv* env); + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_NATIVE_AW_MESSAGE_PORT_SERVICE_IMPL_H_ diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp index 52b3e45..34889e1 100644 --- a/android_webview/native/webview_native.gyp +++ b/android_webview/native/webview_native.gyp @@ -58,6 +58,8 @@ 'aw_http_auth_handler.h', 'aw_media_url_interceptor.cc', 'aw_media_url_interceptor.h', + 'aw_message_port_service_impl.cc', + 'aw_messagE_port_service_impl.h', 'aw_pdf_exporter.cc', 'aw_pdf_exporter.h', 'aw_picture.cc', @@ -131,6 +133,7 @@ '../java/src/org/chromium/android_webview/AwDevToolsServer.java', '../java/src/org/chromium/android_webview/AwFormDatabase.java', '../java/src/org/chromium/android_webview/AwHttpAuthHandler.java', + '../java/src/org/chromium/android_webview/AwMessagePortService.java', '../java/src/org/chromium/android_webview/AwPdfExporter.java', '../java/src/org/chromium/android_webview/AwPicture.java', '../java/src/org/chromium/android_webview/AwQuotaManagerBridge.java', diff --git a/android_webview/renderer/DEPS b/android_webview/renderer/DEPS index 055b5e0..6c1ad13 100644 --- a/android_webview/renderer/DEPS +++ b/android_webview/renderer/DEPS @@ -20,4 +20,6 @@ include_rules = [ "+ui/gfx", "+ui/gl/gpu_memory_buffer.h", + + "+v8/include/v8.h", ] diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc index 24349cd..3771059 100644 --- a/android_webview/renderer/aw_content_renderer_client.cc +++ b/android_webview/renderer/aw_content_renderer_client.cc @@ -8,6 +8,7 @@ #include "android_webview/common/render_view_messages.h" #include "android_webview/common/url_constants.h" #include "android_webview/renderer/aw_key_systems.h" +#include "android_webview/renderer/aw_message_port_client.h" #include "android_webview/renderer/aw_permission_client.h" #include "android_webview/renderer/aw_render_frame_ext.h" #include "android_webview/renderer/aw_render_view_ext.h" @@ -122,6 +123,7 @@ void AwContentRendererClient::RenderFrameCreated( new AwPermissionClient(render_frame); new PrintRenderFrameObserver(render_frame); new AwRenderFrameExt(render_frame); + new AwMessagePortClient(render_frame); // TODO(jam): when the frame tree moves into content and parent() works at // RenderFrame construction, simplify this by just checking parent(). diff --git a/android_webview/renderer/aw_message_port_client.cc b/android_webview/renderer/aw_message_port_client.cc new file mode 100644 index 0000000..c96f322 --- /dev/null +++ b/android_webview/renderer/aw_message_port_client.cc @@ -0,0 +1,70 @@ +// Copyright 2015 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 "android_webview/renderer/aw_message_port_client.h" + +#include "android_webview/common/aw_message_port_messages.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_view.h" +#include "content/public/renderer/v8_value_converter.h" +#include "ipc/ipc_message_macros.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebSerializedScriptValue.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "v8/include/v8.h" + +using blink::WebSerializedScriptValue; +using content::V8ValueConverter; +using std::vector; + +namespace android_webview { + +AwMessagePortClient::AwMessagePortClient(content::RenderFrame* render_frame) + : content::RenderFrameObserver(render_frame) { +} + +AwMessagePortClient::~AwMessagePortClient() { +} + +bool AwMessagePortClient::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AwMessagePortClient, message) + IPC_MESSAGE_HANDLER(AwMessagePortMsg_Message, OnPostMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void AwMessagePortClient::OnPostMessage( + int message_port_id, + const base::string16& message, + const vector<int>& sent_message_port_ids) { + v8::HandleScope handle_scope(blink::mainThreadIsolate()); + blink::WebFrame* main_frame = + render_frame()->GetRenderView()->GetWebView()->mainFrame(); + if (main_frame == nullptr) { + return; + } + v8::Local<v8::Context> context = main_frame->mainWorldScriptContext(); + v8::Context::Scope context_scope(context); + DCHECK(!context.IsEmpty()); + WebSerializedScriptValue v = WebSerializedScriptValue::fromString(message); + v8::Handle<v8::Value> v8value = v.deserialize(); + + scoped_ptr<V8ValueConverter> converter; + converter.reset(V8ValueConverter::create()); + converter->SetDateAllowed(true); + converter->SetRegExpAllowed(true); + base::ListValue result; + result.Append(converter->FromV8Value(v8value, context)); + Send(new AwMessagePortHostMsg_ConvertedMessage(render_frame()->GetRoutingID(), + message_port_id, + result, + sent_message_port_ids)); +} + +} diff --git a/android_webview/renderer/aw_message_port_client.h b/android_webview/renderer/aw_message_port_client.h new file mode 100644 index 0000000..f3a337b --- /dev/null +++ b/android_webview/renderer/aw_message_port_client.h @@ -0,0 +1,35 @@ +// Copyright 2015 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. + +#ifndef ANDROID_WEBVIEW_RENDERER_AW_MESSAGE_PORT_CLIENT_H_ +#define ANDROID_WEBVIEW_RENDERER_AW_MESSAGE_PORT_CLIENT_H_ + +#include <vector> + +#include "base/strings/string16.h" +#include "content/public/renderer/render_frame_observer.h" + +namespace android_webview { + +// Renderer side of Android webview specific message port service. This service +// is used to convert messages from WebSerializedScriptValue to a base value. +class AwMessagePortClient : public content::RenderFrameObserver { + public: + explicit AwMessagePortClient(content::RenderFrame* render_frame); + + private: + virtual ~AwMessagePortClient(); + + // RenderFrameObserver + bool OnMessageReceived(const IPC::Message& message) override; + + void OnPostMessage(int message_port_id, + const base::string16& message, + const std::vector<int>& sent_message_port_ids); + DISALLOW_COPY_AND_ASSIGN(AwMessagePortClient); +}; + +} + +#endif // ANDROID_WEBVIEW_RENDERER_AW_MESSAGE_PORT_CLIENT_H_ diff --git a/content/browser/message_port_message_filter.h b/content/browser/message_port_message_filter.h index 7acc141..cdc8628 100644 --- a/content/browser/message_port_message_filter.h +++ b/content/browser/message_port_message_filter.h @@ -6,16 +6,16 @@ #define CONTENT_BROWSER_MESSAGE_PORT_MESSAGE_FILTER_H_ #include "base/callback.h" -#include "content/browser/message_port_delegate.h" #include "content/common/content_export.h" #include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/message_port_delegate.h" namespace content { // Filter for MessagePort related IPC messages (creating and destroying a // MessagePort, sending a message via a MessagePort etc). class CONTENT_EXPORT MessagePortMessageFilter - : NON_EXPORTED_BASE(public MessagePortDelegate), + : public MessagePortDelegate, public BrowserMessageFilter { public: typedef base::Callback<int(void)> NextRoutingIDCallback; diff --git a/content/browser/message_port_provider.cc b/content/browser/message_port_provider.cc index 12be065..0fd141d 100644 --- a/content/browser/message_port_provider.cc +++ b/content/browser/message_port_provider.cc @@ -12,59 +12,77 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/view_messages.h" +#include "content/public/browser/message_port_delegate.h" namespace content { +namespace { + +void PostMessageOnIOThread(MessagePortMessageFilter* filter, + int routing_id, + ViewMsg_PostMessage_Params* params) { + if (!params->message_port_ids.empty()) { + filter->UpdateMessagePortsWithNewRoutes(params->message_port_ids, + ¶ms->new_routing_ids); + } + filter->Send(new ViewMsg_PostMessageEvent(routing_id, *params)); +} + +} // namespace + +// static void MessagePortProvider::PostMessageToFrame( WebContents* web_contents, const base::string16& source_origin, const base::string16& target_origin, const base::string16& data, const std::vector<int>& ports) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); - RenderViewHost* rvh = web_contents->GetRenderViewHost(); - if (!rvh) - return; + ViewMsg_PostMessage_Params* params = new ViewMsg_PostMessage_Params(); + params->is_data_raw_string = true; + params->data = data; + // Blink requires a source frame to transfer ports. This is why a + // source routing id is set here. See WebDOMMessageEvent::initMessageEvent() + params->source_routing_id = web_contents->GetRoutingID(); + params->source_origin = source_origin; + params->target_origin = target_origin; + params->message_port_ids = ports; - ViewMsg_PostMessage_Params params; - params.is_data_raw_string = true; - params.data = data; - params.source_routing_id = web_contents->GetRoutingID(); - params.source_origin = source_origin; - params.target_origin = target_origin; RenderProcessHostImpl* rph = - static_cast<RenderProcessHostImpl*>( - web_contents->GetRenderProcessHost()); + static_cast<RenderProcessHostImpl*>(web_contents->GetRenderProcessHost()); MessagePortMessageFilter* mf = rph->message_port_message_filter(); - - if (!ports.empty()) { - params.message_port_ids = ports; - mf->UpdateMessagePortsWithNewRoutes(params.message_port_ids, - ¶ms.new_routing_ids); - } - rvh->Send(new ViewMsg_PostMessageEvent( - web_contents->GetRenderViewHost()->GetRoutingID(), - params)); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&PostMessageOnIOThread, + make_scoped_refptr(mf), + web_contents->GetRoutingID(), + base::Owned(params))); } -void MessagePortProvider::CreateMessageChannel(WebContents* web_contents, +// static +void MessagePortProvider::CreateMessageChannel(MessagePortDelegate* delegate, int* port1, int* port2) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - + DCHECK_CURRENTLY_ON(BrowserThread::IO); *port1 = 0; *port2 = 0; - - RenderProcessHostImpl* rph = - static_cast<RenderProcessHostImpl*>( - web_contents->GetRenderProcessHost()); - MessagePortMessageFilter* mf = rph->message_port_message_filter(); MessagePortService* msp = MessagePortService::GetInstance(); - msp->Create(mf->GetNextRoutingID(), mf, port1); - msp->Create(mf->GetNextRoutingID(), mf, port2); + msp->Create(MSG_ROUTING_NONE, delegate, port1); + msp->Create(MSG_ROUTING_NONE, delegate, port2); + // Update the routing number of the message ports to be equal to the message + // port numbers. + msp->UpdateMessagePort(*port1, delegate, *port1); + msp->UpdateMessagePort(*port2, delegate, *port2); msp->Entangle(*port1, *port2); msp->Entangle(*port2, *port1); } +// static +void MessagePortProvider::OnMessagePortDelegateClosing( + MessagePortDelegate* delegate) { + MessagePortService::GetInstance()->OnMessagePortDelegateClosing(delegate); +} + } // namespace content diff --git a/content/browser/message_port_provider_browsertest.cc b/content/browser/message_port_provider_browsertest.cc new file mode 100644 index 0000000..3c9ea5d --- /dev/null +++ b/content/browser/message_port_provider_browsertest.cc @@ -0,0 +1,142 @@ +// Copyright 2015 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 "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "content/browser/message_port_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/message_port_delegate.h" +#include "content/public/browser/message_port_provider.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" + +namespace content { + +// This test verifies the functionality of the Message Port Provider API. + +// A mock class for testing message port provider. +class MockMessagePortDelegate : public MessagePortDelegate { + public: + // A container to hold received messages + struct Message { + int route_id; // the routing id of the target port + base::string16 data; // the message data + std::vector<int> sent_ports; // any transferred ports + }; + + typedef std::vector<Message> Messages; + + MockMessagePortDelegate() { } + ~MockMessagePortDelegate() override { } + + // MessagePortDelegate implementation + void SendMessage(int route_id, + const base::string16& message, + const std::vector<int>& sent_message_port_ids) override { + Message m; + m.route_id = route_id; + m.data = message; + m.sent_ports = sent_message_port_ids; + messages_.push_back(m); + } + + void SendMessagesAreQueued(int route_id) override { } + + const Messages& getReceivedMessages() { + return messages_; + } + private: + Messages messages_; + + DISALLOW_COPY_AND_ASSIGN(MockMessagePortDelegate); +}; + + +class MessagePortProviderBrowserTest : public ContentBrowserTest { +}; + +// Verify that messages can be posted to main frame. +IN_PROC_BROWSER_TEST_F(MessagePortProviderBrowserTest, PostMessage) { + const std::string data = + "<!DOCTYPE html><html><body>" + " <script type=\"text/javascript\">" + " onmessage = function (e) { document.title = e.data; }" + " </script>" + "</body></html>"; + const base::string16 target_origin(base::UTF8ToUTF16("http://baseurl")); + const GURL base_url(target_origin); + const GURL history_url; + // Load data. Blocks until it is done. + content::LoadDataWithBaseURL(shell(), history_url, data, base_url); + const base::string16 source_origin(base::UTF8ToUTF16("source")); + const base::string16 message(base::UTF8ToUTF16("success")); + const std::vector<int> ports; + content::TitleWatcher title_watcher(shell()->web_contents(), message); + MessagePortProvider::PostMessageToFrame(shell()->web_contents(), + source_origin, + target_origin, + message, + ports); + EXPECT_EQ(message, title_watcher.WaitAndGetTitle()); +} + +namespace { + +void VerifyCreateChannelOnIOThread(base::WaitableEvent* event) { + + const base::char16 MESSAGE1[] = { 0x1000, 0 }; + const base::char16 MESSAGE2[] = { 0x1001, 0 }; + + MockMessagePortDelegate delegate; + int port1; + int port2; + + MessagePortProvider::CreateMessageChannel(&delegate, &port1, &port2); + MessagePortService* service = MessagePortService::GetInstance(); + // Send a message to port1 transferring no ports. + std::vector<int> sent_ports; + service->PostMessage(port1, base::string16(MESSAGE1), sent_ports); + // Verify that message is received + const MockMessagePortDelegate::Messages& received = + delegate.getReceivedMessages(); + EXPECT_EQ(received.size(), 1u); + // Verify that message sent to port1 is received by entangled port, which is + // port2. + EXPECT_EQ(received[0].route_id, port2); + EXPECT_EQ(received[0].data, MESSAGE1); + EXPECT_EQ(received[0].sent_ports.size(), 0u); + + // Create a new channel, and transfer one of its ports to port2, making sure + // the transferred port is received. + int port3; + int port4; + MessagePortProvider::CreateMessageChannel(&delegate, &port3, &port4); + sent_ports.push_back(port3); + service->PostMessage(port1, base::string16(MESSAGE2), sent_ports); + EXPECT_EQ(received.size(), 2u); + EXPECT_EQ(received[1].route_id, port2); + EXPECT_EQ(received[1].data, MESSAGE2); + EXPECT_EQ(received[1].sent_ports.size(), 1u); + EXPECT_EQ(received[1].sent_ports[0], port3); + + event->Signal(); +} + +} // namespace + +// Verify that a message channel can be created and used for exchanging +// messages. +IN_PROC_BROWSER_TEST_F(MessagePortProviderBrowserTest, CreateChannel) { + base::WaitableEvent event(true, false); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&VerifyCreateChannelOnIOThread, &event)); + event.Wait(); +} + +} // namespace content diff --git a/content/browser/message_port_service.cc b/content/browser/message_port_service.cc index 42a3051..382f5b6 100644 --- a/content/browser/message_port_service.cc +++ b/content/browser/message_port_service.cc @@ -4,9 +4,9 @@ #include "content/browser/message_port_service.h" -#include "content/browser/message_port_delegate.h" #include "content/common/message_port_messages.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/message_port_delegate.h" namespace content { diff --git a/content/browser/message_port_service.h b/content/browser/message_port_service.h index 623b26b..5623e94 100644 --- a/content/browser/message_port_service.h +++ b/content/browser/message_port_service.h @@ -12,12 +12,13 @@ #include "base/basictypes.h" #include "base/memory/singleton.h" #include "base/strings/string16.h" +#include "content/common/content_export.h" #include "ipc/ipc_message.h" namespace content { class MessagePortDelegate; -class MessagePortService { +class CONTENT_EXPORT MessagePortService { public: typedef std::vector<std::pair<base::string16, std::vector<int> > > QueuedMessages; diff --git a/content/browser/navigator_connect/navigator_connect_context.h b/content/browser/navigator_connect/navigator_connect_context.h index db4edbc..3794867 100644 --- a/content/browser/navigator_connect/navigator_connect_context.h +++ b/content/browser/navigator_connect/navigator_connect_context.h @@ -7,8 +7,8 @@ #include <map> #include "base/memory/ref_counted.h" -#include "content/browser/message_port_delegate.h" #include "content/common/service_worker/service_worker_status_code.h" +#include "content/public/browser/message_port_delegate.h" namespace content { diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 9fc7832..33d64df 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -150,6 +150,7 @@ 'public/browser/media_device_id.cc', 'public/browser/media_device_id.h', 'public/browser/memory_pressure_observer.h', + 'public/browser/message_port_delegate.h', 'public/browser/message_port_provider.h', 'public/browser/native_web_keyboard_event.h', 'public/browser/navigation_controller.cc', @@ -935,7 +936,6 @@ 'browser/media/webrtc_identity_store_backend.h', 'browser/manifest/manifest_manager_host.cc', 'browser/manifest/manifest_manager_host.h', - 'browser/message_port_delegate.h', 'browser/message_port_message_filter.cc', 'browser/message_port_message_filter.h', 'browser/message_port_provider.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index c1ff6e9..cbbfa23 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -218,6 +218,7 @@ 'browser/media/media_browsertest.h', 'browser/media/media_canplaytype_browsertest.cc', 'browser/media/media_source_browsertest.cc', + 'browser/message_port_provider_browsertest.cc', 'browser/net_info_browsertest.cc', 'browser/plugin_browsertest.cc', 'browser/renderer_host/input/touch_action_browsertest.cc', diff --git a/content/browser/message_port_delegate.h b/content/public/browser/message_port_delegate.h index 8f6ad1b..70abbeb 100644 --- a/content/browser/message_port_delegate.h +++ b/content/public/browser/message_port_delegate.h @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_MESSAGE_PORT_DELEGATE_H_ -#define CONTENT_BROWSER_MESSAGE_PORT_DELEGATE_H_ +#ifndef CONTENT_PUBLIC_BROWSER_MESSAGE_PORT_DELEGATE_H_ +#define CONTENT_PUBLIC_BROWSER_MESSAGE_PORT_DELEGATE_H_ #include <vector> #include "base/strings/string16.h" +#include "content/common/content_export.h" namespace content { @@ -15,7 +16,7 @@ namespace content { // correct renderer. Delegates are responsible for managing their own lifetime, // and should call MessagePortService::OnMessagePortDelegateClosing if they are // destroyed while there are still message ports associated with them. -class MessagePortDelegate { +class CONTENT_EXPORT MessagePortDelegate { public: // Sends a message to the given route. Implementations are responsible for // updating MessagePortService with new routes for the sent message ports. @@ -23,7 +24,7 @@ class MessagePortDelegate { const base::string16& message, const std::vector<int>& sent_message_port_ids) = 0; - // Sends a "messages are queued" IPC to the given route. + // Requests messages to the given route to be queued. virtual void SendMessagesAreQueued(int route_id) = 0; protected: @@ -32,4 +33,4 @@ class MessagePortDelegate { } // namespace content -#endif // CONTENT_BROWSER_MESSAGE_PORT_DELEGATE_H_ +#endif // CONTENT_PUBLIC_BROWSER_MESSAGE_PORT_DELEGATE_H_ diff --git a/content/public/browser/message_port_provider.h b/content/public/browser/message_port_provider.h index aab9269..70fbe11 100644 --- a/content/public/browser/message_port_provider.h +++ b/content/public/browser/message_port_provider.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// 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. @@ -14,6 +14,7 @@ namespace content { +class MessagePortDelegate; class WebContents; // An interface consisting of methods that can be called to use Message ports. @@ -24,7 +25,7 @@ class CONTENT_EXPORT MessagePortProvider { // part of the message. // See https://html.spec.whatwg.org/multipage/comms.html#messageevent for // further information on message events. - // Should be called on IO thread. + // Should be called on UI thread. static void PostMessageToFrame(WebContents* web_contents, const base::string16& source_origin, const base::string16& target_origin, @@ -35,10 +36,15 @@ class CONTENT_EXPORT MessagePortProvider { // associated with this message channel. // See https://html.spec.whatwg.org/multipage/comms.html#messagechannel // Should be called on IO thread. - static void CreateMessageChannel(WebContents* web_contents, + // The message ports that are created will have their routing id numbers equal + // to the message port numbers. + static void CreateMessageChannel(MessagePortDelegate* delegate, int* port1, int* port2); + // Cleanup the message ports that belong to the closing delegate. + static void OnMessagePortDelegateClosing(MessagePortDelegate * delegate); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePortProvider); }; diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index b22c7b3..48dd9af 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -113,6 +113,7 @@ enum IPCMessageStart { BluetoothMsgStart, NavigatorConnectMsgStart, CastMediaMsgStart, + AwMessagePortMsgStart, LastIPCMsgStart // Must come last. }; |