| Index: content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java b/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
|
| index 30e50d02e8a3cdbd6f60a0ac75b155cf90cd1162..17a60d78e9cc863ac56191e48b66cc6ca926beb8 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java
|
| @@ -9,6 +9,8 @@ import android.os.Looper;
|
| import android.os.Message;
|
|
|
| import org.chromium.base.Log;
|
| +import org.chromium.base.annotations.CalledByNative;
|
| +import org.chromium.base.annotations.JNINamespace;
|
| import org.chromium.content_public.browser.MessagePort;
|
|
|
| import java.util.Arrays;
|
| @@ -19,16 +21,6 @@ import java.util.Arrays;
|
| *
|
| * State management:
|
| *
|
| - * Initially a message port will be in a pending state. It will be ready once it is created in
|
| - * content/ (in IO thread) and a message port id is assigned.
|
| - * A pending message port cannnot be transferred, and cannot send or receive messages. However,
|
| - * these details are hidden from the user. If a message port is in the pending state:
|
| - * 1. Any messages posted in this port will be queued until the port is ready
|
| - * 2. Transferring the port using a message channel will cause the message (and any subsequent
|
| - * messages sent) to be queued until the port is ready
|
| - * 3. Transferring the pending port via postMessageToFrame will cause the message (and all
|
| - * subsequent messages posted via postMessageToFrame) to be queued until the port is ready.
|
| - *
|
| * A message port can be in transferred state while a transfer is pending or complete. An
|
| * application cannot use a transferred port to post messages. If a transferred port
|
| * receives messages, they will be queued. This state is not visible to embedder app.
|
| @@ -48,7 +40,7 @@ import java.util.Arrays;
|
| * the code below
|
| * 1. var c1 = new MessageChannel();
|
| * 2. var c2 = new MessageChannel();
|
| - * 3. c1.port2.onmessage= function(e) { console.log("1"); }
|
| + * 3. c1.port2.onmessage = function(e) { console.log("1"); }
|
| * 4. c2.port2.onmessage = function(e) {
|
| * 5. e.ports[0].onmessage = function(f) {
|
| * 6. console.log("3");
|
| @@ -73,25 +65,13 @@ import java.util.Arrays;
|
| * transferring data. As a return, it simplifies implementation and prevents hard
|
| * to debug, racy corner cases while receiving/sending data.
|
| */
|
| -public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMessageSenderDelegate {
|
| - private static final String TAG = "MessagePort";
|
| - private static final int PENDING = -1;
|
| -
|
| - // the what value for POST_MESSAGE
|
| - private static final int POST_MESSAGE = 1;
|
| +@JNINamespace("content")
|
| +public class AppWebMessagePort implements MessagePort {
|
| + private static final String TAG = "AppWebMessagePort";
|
| + private static final long UNINITIALIZED_PORT_NATIVE_PTR = 0;
|
|
|
| - private static class PostMessageFromWeb {
|
| - public AppWebMessagePort port;
|
| - public String message;
|
| - public AppWebMessagePort[] sentPorts;
|
| -
|
| - public PostMessageFromWeb(
|
| - AppWebMessagePort port, String message, AppWebMessagePort[] sentPorts) {
|
| - this.port = port;
|
| - this.message = message;
|
| - this.sentPorts = sentPorts;
|
| - }
|
| - }
|
| + // The |what| value for handleMessage.
|
| + private static final int MESSAGES_AVAILABLE = 1;
|
|
|
| // Implements the handler to handle messageport messages received from web.
|
| // These messages are received on IO thread and normally handled in main
|
| @@ -102,9 +82,9 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| }
|
| @Override
|
| public void handleMessage(Message msg) {
|
| - if (msg.what == POST_MESSAGE) {
|
| - PostMessageFromWeb m = (PostMessageFromWeb) msg.obj;
|
| - m.port.onMessage(m.message, m.sentPorts);
|
| + if (msg.what == MESSAGES_AVAILABLE) {
|
| + AppWebMessagePort port = (AppWebMessagePort) msg.obj;
|
| + port.dispatchReceivedMessages();
|
| return;
|
| }
|
| throw new IllegalStateException("undefined message");
|
| @@ -114,35 +94,38 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| private static final MessageHandler sDefaultHandler =
|
| new MessageHandler(Looper.getMainLooper());
|
|
|
| - private int mPortId = PENDING;
|
| + private long mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
|
| private MessageCallback mMessageCallback;
|
| - private AppWebMessagePortService mMessagePortService;
|
| private boolean mClosed;
|
| private boolean mTransferred;
|
| private boolean mStarted;
|
| - private boolean mReleasedMessages;
|
| - private PostMessageSender mPostMessageSender;
|
| private MessageHandler mHandler;
|
| private final Object mLock = new Object();
|
|
|
| - public AppWebMessagePort(AppWebMessagePortService messagePortService) {
|
| - mMessagePortService = messagePortService;
|
| - mPostMessageSender = new PostMessageSender(this, mMessagePortService);
|
| - mMessagePortService.addObserver(mPostMessageSender);
|
| + // Called to create an entangled pair of ports.
|
| + public static AppWebMessagePort[] createPair() {
|
| + AppWebMessagePort[] ports =
|
| + new AppWebMessagePort[] { new AppWebMessagePort(), new AppWebMessagePort() };
|
| + nativeInitializeAppWebMessagePortPair(ports);
|
| + return ports;
|
| }
|
|
|
| @Override
|
| public boolean isReady() {
|
| - return mPortId != PENDING;
|
| + return mNativeAppWebMessagePort != UNINITIALIZED_PORT_NATIVE_PTR;
|
| }
|
|
|
| - public int portId() {
|
| - return mPortId;
|
| + @CalledByNative
|
| + private void setNativeAppWebMessagePort(long nativeAppWebMessagePort) {
|
| + mNativeAppWebMessagePort = nativeAppWebMessagePort;
|
| }
|
|
|
| - public void setPortId(int id) {
|
| - mPortId = id;
|
| - releaseMessages();
|
| + @CalledByNative
|
| + private long releaseNativePortForTransfer() {
|
| + mTransferred = true;
|
| + long port = mNativeAppWebMessagePort;
|
| + mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
|
| + return port;
|
| }
|
|
|
| @Override
|
| @@ -150,16 +133,19 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| if (mTransferred) {
|
| throw new IllegalStateException("Port is already transferred");
|
| }
|
| + if (mClosed) return;
|
| + mClosed = true;
|
| + // Synchronize with dispatchReceivedMessages to ensure that the native
|
| + // port is not closed too soon, but avoid holding mLock while calling
|
| + // nativeCloseMessagePort as that could result in a dead-lock (racing
|
| + // with onMessagesAvailable).
|
| + long port = UNINITIALIZED_PORT_NATIVE_PTR;
|
| synchronized (mLock) {
|
| - if (mClosed) return;
|
| - mClosed = true;
|
| + port = mNativeAppWebMessagePort;
|
| + mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
|
| }
|
| - // If the port is already ready, and no messages are waiting in the
|
| - // queue to be transferred, onPostMessageQueueEmpty() callback is not
|
| - // received (it is received only after messages are purged). In this
|
| - // case do the cleanup here.
|
| - if (isReady() && mPostMessageSender.isMessageQueueEmpty()) {
|
| - cleanup();
|
| + if (port != UNINITIALIZED_PORT_NATIVE_PTR) {
|
| + nativeCloseMessagePort(port);
|
| }
|
| }
|
|
|
| @@ -173,10 +159,6 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| return mTransferred;
|
| }
|
|
|
| - public void setTransferred() {
|
| - mTransferred = true;
|
| - }
|
| -
|
| @Override
|
| public boolean isStarted() {
|
| return mStarted;
|
| @@ -185,6 +167,9 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| // Only called on UI thread
|
| @Override
|
| public void setMessageCallback(MessageCallback messageCallback, Handler handler) {
|
| + if (isClosed() || isTransferred()) {
|
| + throw new IllegalStateException("Port is already closed or transferred");
|
| + }
|
| mStarted = true;
|
| synchronized (mLock) {
|
| mMessageCallback = messageCallback;
|
| @@ -192,40 +177,41 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| mHandler = new MessageHandler(handler.getLooper());
|
| }
|
| }
|
| - releaseMessages();
|
| + nativeStartReceivingMessages(mNativeAppWebMessagePort);
|
| }
|
|
|
| - // Only called on IO thread.
|
| - public void onReceivedMessage(String message, AppWebMessagePort[] sentPorts) {
|
| + // Called on a background thread.
|
| + @CalledByNative
|
| + private void onMessagesAvailable() {
|
| synchronized (mLock) {
|
| - PostMessageFromWeb m = new PostMessageFromWeb(this, message, sentPorts);
|
| Handler handler = mHandler != null ? mHandler : sDefaultHandler;
|
| - Message msg = handler.obtainMessage(POST_MESSAGE, m);
|
| + Message msg = handler.obtainMessage(MESSAGES_AVAILABLE, this);
|
| handler.sendMessage(msg);
|
| }
|
| }
|
|
|
| - private void releaseMessages() {
|
| - if (mReleasedMessages || !isReady() || mMessageCallback == null) {
|
| + // This method is called by nativeDispatchNextMessage while mLock is held.
|
| + @CalledByNative
|
| + private void onReceivedMessage(String message, AppWebMessagePort[] ports) {
|
| + if (mMessageCallback == null) {
|
| + Log.w(TAG, "No handler set for port [" + mNativeAppWebMessagePort
|
| + + "], dropping message " + message);
|
| return;
|
| }
|
| - mReleasedMessages = true;
|
| - mMessagePortService.releaseMessages(mPortId);
|
| - }
|
| -
|
| - // This method may be called on a different thread than UI thread.
|
| - public void onMessage(String message, AppWebMessagePort[] ports) {
|
| - synchronized (mLock) {
|
| - if (isClosed()) {
|
| - Log.w(TAG, "Port [" + mPortId + "] received message in closed state");
|
| - return;
|
| - }
|
| - if (mMessageCallback == null) {
|
| - Log.w(TAG,
|
| - "No handler set for port [" + mPortId + "], dropping message " + message);
|
| - return;
|
| + mMessageCallback.onMessage(message, ports);
|
| + }
|
| +
|
| + // This method may be called on either the UI thread or a background thread.
|
| + private void dispatchReceivedMessages() {
|
| + // Dispatch all of the available messages unless interrupted by close().
|
| + // NOTE: nativeDispatchNextMessage returns true and calls onReceivedMessage
|
| + // if a message is available else it returns false.
|
| + while (true) {
|
| + synchronized (mLock) {
|
| + if (!(isReady() && nativeDispatchNextMessage(mNativeAppWebMessagePort))) {
|
| + break;
|
| + }
|
| }
|
| - mMessageCallback.onMessage(message, ports);
|
| }
|
| }
|
|
|
| @@ -240,37 +226,24 @@ public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes
|
| if (port.equals(this)) {
|
| throw new IllegalStateException("Source port cannot be transferred");
|
| }
|
| + if (port.isClosed() || port.isTransferred()) {
|
| + throw new IllegalStateException("Port is already closed or transferred");
|
| + }
|
| + if (port.isStarted()) {
|
| + throw new IllegalStateException("Port is already started");
|
| + }
|
| }
|
| ports = Arrays.copyOf(sentPorts, sentPorts.length, AppWebMessagePort[].class);
|
| }
|
| mStarted = true;
|
| - mPostMessageSender.postMessage(null, message, null, ports);
|
| + nativePostMessage(mNativeAppWebMessagePort, message, ports);
|
| }
|
|
|
| - // Implements PostMessageSender.PostMessageSenderDelegate interface method.
|
| - @Override
|
| - public boolean isPostMessageSenderReady() {
|
| - return isReady();
|
| - }
|
| + private static native void nativeInitializeAppWebMessagePortPair(AppWebMessagePort[] ports);
|
|
|
| - // Implements PostMessageSender.PostMessageSenderDelegate interface method.
|
| - @Override
|
| - public void onPostMessageQueueEmpty() {
|
| - if (isClosed()) {
|
| - cleanup();
|
| - }
|
| - }
|
| -
|
| - // Implements PostMessageSender.PostMessageSenderDelegate interface method.
|
| - @Override
|
| - public void postMessageToWeb(
|
| - String frameName, String message, String targetOrigin, int[] sentPortIds) {
|
| - mMessagePortService.postMessage(mPortId, message, sentPortIds);
|
| - }
|
| -
|
| - private void cleanup() {
|
| - mMessagePortService.removeObserver(mPostMessageSender);
|
| - mPostMessageSender = null;
|
| - mMessagePortService.closePort(mPortId);
|
| - }
|
| + private native void nativeCloseMessagePort(long nativeAppWebMessagePort);
|
| + private native void nativePostMessage(long nativeAppWebMessagePort, String message,
|
| + AppWebMessagePort[] ports);
|
| + private native boolean nativeDispatchNextMessage(long nativeAppWebMessagePort);
|
| + private native void nativeStartReceivingMessages(long nativeAppWebMessagePort);
|
| }
|
|
|