Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/AppWebMessagePort.java

Issue 2422793002: HTML MessagePort as mojo::MessagePipeHandle (Closed)
Patch Set: Add comments about threading in message_port.h Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser; 5 package org.chromium.content.browser;
6 6
7 import android.os.Handler; 7 import android.os.Handler;
8 import android.os.Looper; 8 import android.os.Looper;
9 import android.os.Message; 9 import android.os.Message;
10 10
11 import org.chromium.base.Log; 11 import org.chromium.base.Log;
12 import org.chromium.base.annotations.CalledByNative;
13 import org.chromium.base.annotations.JNINamespace;
12 import org.chromium.content_public.browser.MessagePort; 14 import org.chromium.content_public.browser.MessagePort;
13 15
14 import java.util.Arrays; 16 import java.util.Arrays;
15 17
16 /** 18 /**
17 * Represents the MessageChannel MessagePort object. Inspired from 19 * Represents the MessageChannel MessagePort object. Inspired from
18 * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.htm l#message-channels 20 * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.htm l#message-channels
19 * 21 *
20 * State management: 22 * State management:
21 * 23 *
22 * Initially a message port will be in a pending state. It will be ready once it is created in
23 * content/ (in IO thread) and a message port id is assigned.
24 * A pending message port cannnot be transferred, and cannot send or receive mes sages. However,
25 * these details are hidden from the user. If a message port is in the pending s tate:
26 * 1. Any messages posted in this port will be queued until the port is ready
27 * 2. Transferring the port using a message channel will cause the message (and any subsequent
28 * messages sent) to be queued until the port is ready
29 * 3. Transferring the pending port via postMessageToFrame will cause the messag e (and all
30 * subsequent messages posted via postMessageToFrame) to be queued until the port is ready.
31 *
32 * A message port can be in transferred state while a transfer is pending or com plete. An 24 * A message port can be in transferred state while a transfer is pending or com plete. An
33 * application cannot use a transferred port to post messages. If a transferred port 25 * application cannot use a transferred port to post messages. If a transferred port
34 * receives messages, they will be queued. This state is not visible to embedder app. 26 * receives messages, they will be queued. This state is not visible to embedder app.
35 * 27 *
36 * A message port should be closed by the app when it is not needed any more. Th is will free 28 * A message port should be closed by the app when it is not needed any more. Th is will free
37 * any resources used by it. A closed port cannot receive/send messages and cann ot be transferred. 29 * any resources used by it. A closed port cannot receive/send messages and cann ot be transferred.
38 * close() can be called multiple times. A transferred port cannot be closed by the application, 30 * close() can be called multiple times. A transferred port cannot be closed by the application,
39 * since the ownership is also transferred during the transfer. Closing a transf erred port will 31 * since the ownership is also transferred during the transfer. Closing a transf erred port will
40 * throw an exception. 32 * throw an exception.
41 * 33 *
42 * The fact that messages can be handled on a separate thread means that thread 34 * The fact that messages can be handled on a separate thread means that thread
43 * synchronization is important. All methods are called on UI thread except as n oted. 35 * synchronization is important. All methods are called on UI thread except as n oted.
44 * 36 *
45 * Restrictions: 37 * Restrictions:
46 * The HTML5 message protocol is very flexible in transferring ports. However, t his 38 * The HTML5 message protocol is very flexible in transferring ports. However, t his
47 * sometimes leads to surprising behavior. For example, in current version of ch rome (m41) 39 * sometimes leads to surprising behavior. For example, in current version of ch rome (m41)
48 * the code below 40 * the code below
49 * 1. var c1 = new MessageChannel(); 41 * 1. var c1 = new MessageChannel();
50 * 2. var c2 = new MessageChannel(); 42 * 2. var c2 = new MessageChannel();
51 * 3. c1.port2.onmessage= function(e) { console.log("1"); } 43 * 3. c1.port2.onmessage = function(e) { console.log("1"); }
52 * 4. c2.port2.onmessage = function(e) { 44 * 4. c2.port2.onmessage = function(e) {
53 * 5. e.ports[0].onmessage = function(f) { 45 * 5. e.ports[0].onmessage = function(f) {
54 * 6. console.log("3"); 46 * 6. console.log("3");
55 * 7. } 47 * 7. }
56 * 8. } 48 * 8. }
57 * 9. c1.port1.postMessage("test"); 49 * 9. c1.port1.postMessage("test");
58 * 10. c2.port1.postMessage("test2",[c1.port2]) 50 * 10. c2.port1.postMessage("test2",[c1.port2])
59 * 51 *
60 * prints 1 or 3 depending on whether or not line 10 is included in code. Furthe r if 52 * prints 1 or 3 depending on whether or not line 10 is included in code. Furthe r if
61 * it gets executed with a timeout, depending on timeout value, the printout val ue 53 * it gets executed with a timeout, depending on timeout value, the printout val ue
62 * changes. 54 * changes.
63 * 55 *
64 * To prevent such problems, this implementation limits the transfer of ports 56 * To prevent such problems, this implementation limits the transfer of ports
65 * as below: 57 * as below:
66 * A port is put to a "started" state if: 58 * A port is put to a "started" state if:
67 * 1. The port is ever used to post a message, or 59 * 1. The port is ever used to post a message, or
68 * 2. The port was ever registered a handler to receive a message. 60 * 2. The port was ever registered a handler to receive a message.
69 * A started port cannot be transferred. 61 * A started port cannot be transferred.
70 * 62 *
71 * This restriction should not impact postmessage functionality in a big way, 63 * This restriction should not impact postmessage functionality in a big way,
72 * because an app can still create as many channels as it wants to and use it fo r 64 * because an app can still create as many channels as it wants to and use it fo r
73 * transferring data. As a return, it simplifies implementation and prevents har d 65 * transferring data. As a return, it simplifies implementation and prevents har d
74 * to debug, racy corner cases while receiving/sending data. 66 * to debug, racy corner cases while receiving/sending data.
75 */ 67 */
76 public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes sageSenderDelegate { 68 @JNINamespace("content")
77 private static final String TAG = "MessagePort"; 69 public class AppWebMessagePort implements MessagePort {
78 private static final int PENDING = -1; 70 private static final String TAG = "AppWebMessagePort";
71 private static final long UNINITIALIZED_PORT_NATIVE_PTR = 0;
79 72
80 // the what value for POST_MESSAGE 73 // The |what| value for handleMessage.
81 private static final int POST_MESSAGE = 1; 74 private static final int MESSAGES_AVAILABLE = 1;
82
83 private static class PostMessageFromWeb {
84 public AppWebMessagePort port;
85 public String message;
86 public AppWebMessagePort[] sentPorts;
87
88 public PostMessageFromWeb(
89 AppWebMessagePort port, String message, AppWebMessagePort[] sent Ports) {
90 this.port = port;
91 this.message = message;
92 this.sentPorts = sentPorts;
93 }
94 }
95 75
96 // Implements the handler to handle messageport messages received from web. 76 // Implements the handler to handle messageport messages received from web.
97 // These messages are received on IO thread and normally handled in main 77 // These messages are received on IO thread and normally handled in main
98 // thread however, alternatively application can pass a handler to execute t hem. 78 // thread however, alternatively application can pass a handler to execute t hem.
99 private static class MessageHandler extends Handler { 79 private static class MessageHandler extends Handler {
100 public MessageHandler(Looper looper) { 80 public MessageHandler(Looper looper) {
101 super(looper); 81 super(looper);
102 } 82 }
103 @Override 83 @Override
104 public void handleMessage(Message msg) { 84 public void handleMessage(Message msg) {
105 if (msg.what == POST_MESSAGE) { 85 if (msg.what == MESSAGES_AVAILABLE) {
106 PostMessageFromWeb m = (PostMessageFromWeb) msg.obj; 86 AppWebMessagePort port = (AppWebMessagePort) msg.obj;
107 m.port.onMessage(m.message, m.sentPorts); 87 port.dispatchReceivedMessages();
108 return; 88 return;
109 } 89 }
110 throw new IllegalStateException("undefined message"); 90 throw new IllegalStateException("undefined message");
111 } 91 }
112 } 92 }
113 // The default message handler 93 // The default message handler
114 private static final MessageHandler sDefaultHandler = 94 private static final MessageHandler sDefaultHandler =
115 new MessageHandler(Looper.getMainLooper()); 95 new MessageHandler(Looper.getMainLooper());
116 96
117 private int mPortId = PENDING; 97 private long mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
118 private MessageCallback mMessageCallback; 98 private MessageCallback mMessageCallback;
119 private AppWebMessagePortService mMessagePortService;
120 private boolean mClosed; 99 private boolean mClosed;
121 private boolean mTransferred; 100 private boolean mTransferred;
122 private boolean mStarted; 101 private boolean mStarted;
123 private boolean mReleasedMessages;
124 private PostMessageSender mPostMessageSender;
125 private MessageHandler mHandler; 102 private MessageHandler mHandler;
126 private final Object mLock = new Object(); 103 private final Object mLock = new Object();
127 104
128 public AppWebMessagePort(AppWebMessagePortService messagePortService) { 105 // Called to create an entangled pair of ports.
129 mMessagePortService = messagePortService; 106 public static AppWebMessagePort[] createPair() {
130 mPostMessageSender = new PostMessageSender(this, mMessagePortService); 107 AppWebMessagePort[] ports =
131 mMessagePortService.addObserver(mPostMessageSender); 108 new AppWebMessagePort[] { new AppWebMessagePort(), new AppWebMessage Port() };
109 nativeInitializeAppWebMessagePortPair(ports);
110 return ports;
132 } 111 }
133 112
134 @Override 113 @Override
135 public boolean isReady() { 114 public boolean isReady() {
136 return mPortId != PENDING; 115 return mNativeAppWebMessagePort != UNINITIALIZED_PORT_NATIVE_PTR;
137 } 116 }
138 117
139 public int portId() { 118 @CalledByNative
140 return mPortId; 119 private void setNativeAppWebMessagePort(long nativeAppWebMessagePort) {
120 mNativeAppWebMessagePort = nativeAppWebMessagePort;
141 } 121 }
142 122
143 public void setPortId(int id) { 123 @CalledByNative
144 mPortId = id; 124 private long releaseNativePortForTransfer() {
145 releaseMessages(); 125 mTransferred = true;
126 long port = mNativeAppWebMessagePort;
127 mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
128 return port;
146 } 129 }
147 130
148 @Override 131 @Override
149 public void close() { 132 public void close() {
150 if (mTransferred) { 133 if (mTransferred) {
151 throw new IllegalStateException("Port is already transferred"); 134 throw new IllegalStateException("Port is already transferred");
152 } 135 }
136 if (mClosed) return;
137 mClosed = true;
138 // Synchronize with dispatchReceivedMessages to ensure that the native
139 // port is not closed too soon, but avoid holding mLock while calling
140 // nativeCloseMessagePort as that could result in a dead-lock (racing
141 // with onMessagesAvailable).
142 long port = UNINITIALIZED_PORT_NATIVE_PTR;
153 synchronized (mLock) { 143 synchronized (mLock) {
154 if (mClosed) return; 144 port = mNativeAppWebMessagePort;
155 mClosed = true; 145 mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR;
156 } 146 }
157 // If the port is already ready, and no messages are waiting in the 147 if (port != UNINITIALIZED_PORT_NATIVE_PTR) {
158 // queue to be transferred, onPostMessageQueueEmpty() callback is not 148 nativeCloseMessagePort(port);
159 // received (it is received only after messages are purged). In this
160 // case do the cleanup here.
161 if (isReady() && mPostMessageSender.isMessageQueueEmpty()) {
162 cleanup();
163 } 149 }
164 } 150 }
165 151
166 @Override 152 @Override
167 public boolean isClosed() { 153 public boolean isClosed() {
168 return mClosed; 154 return mClosed;
169 } 155 }
170 156
171 @Override 157 @Override
172 public boolean isTransferred() { 158 public boolean isTransferred() {
173 return mTransferred; 159 return mTransferred;
174 } 160 }
175 161
176 public void setTransferred() {
177 mTransferred = true;
178 }
179
180 @Override 162 @Override
181 public boolean isStarted() { 163 public boolean isStarted() {
182 return mStarted; 164 return mStarted;
183 } 165 }
184 166
185 // Only called on UI thread 167 // Only called on UI thread
186 @Override 168 @Override
187 public void setMessageCallback(MessageCallback messageCallback, Handler hand ler) { 169 public void setMessageCallback(MessageCallback messageCallback, Handler hand ler) {
188 mStarted = true; 170 mStarted = true;
sgurun-gerrit only 2017/02/10 22:02:48 Another bug is that the user can call setMessageCa
sgurun-gerrit only 2017/02/10 23:14:31 I mean not early out but throw.
sgurun-gerrit only 2017/02/10 23:14:31 i.e. Throw not early out.
darin (slow to review) 2017/02/14 19:50:05 Got it. I can add such a check.
189 synchronized (mLock) { 171 synchronized (mLock) {
190 mMessageCallback = messageCallback; 172 mMessageCallback = messageCallback;
191 if (handler != null) { 173 if (handler != null) {
192 mHandler = new MessageHandler(handler.getLooper()); 174 mHandler = new MessageHandler(handler.getLooper());
193 } 175 }
194 } 176 }
195 releaseMessages(); 177 nativeStartReceivingMessages(mNativeAppWebMessagePort);
196 } 178 }
197 179
198 // Only called on IO thread. 180 // Called on a background thread.
199 public void onReceivedMessage(String message, AppWebMessagePort[] sentPorts) { 181 @CalledByNative
182 private void onMessagesAvailable() {
200 synchronized (mLock) { 183 synchronized (mLock) {
201 PostMessageFromWeb m = new PostMessageFromWeb(this, message, sentPor ts);
202 Handler handler = mHandler != null ? mHandler : sDefaultHandler; 184 Handler handler = mHandler != null ? mHandler : sDefaultHandler;
203 Message msg = handler.obtainMessage(POST_MESSAGE, m); 185 Message msg = handler.obtainMessage(MESSAGES_AVAILABLE, this);
204 handler.sendMessage(msg); 186 handler.sendMessage(msg);
205 } 187 }
206 } 188 }
207 189
208 private void releaseMessages() { 190 // This method is called by nativeDispatchNextMessage while mLock is held.
209 if (mReleasedMessages || !isReady() || mMessageCallback == null) { 191 @CalledByNative
192 private void onReceivedMessage(String message, AppWebMessagePort[] ports) {
193 if (mMessageCallback == null) {
194 Log.w(TAG, "No handler set for port [" + mNativeAppWebMessagePort
195 + "], dropping message " + message);
210 return; 196 return;
211 } 197 }
212 mReleasedMessages = true; 198 mMessageCallback.onMessage(message, ports);
213 mMessagePortService.releaseMessages(mPortId);
214 } 199 }
215 200
216 // This method may be called on a different thread than UI thread. 201 // This method may be called on either the UI thread or a background thread.
217 public void onMessage(String message, AppWebMessagePort[] ports) { 202 private void dispatchReceivedMessages() {
218 synchronized (mLock) { 203 // Dispatch all of the available messages unless interrupted by close().
219 if (isClosed()) { 204 // NOTE: nativeDispatchNextMessage returns true and calls onReceivedMess age
220 Log.w(TAG, "Port [" + mPortId + "] received message in closed st ate"); 205 // if a message is available else it returns false.
221 return; 206 while (true) {
207 synchronized (mLock) {
208 if (!(isReady() && nativeDispatchNextMessage(mNativeAppWebMessag ePort))) {
Yusuf 2017/02/10 21:43:50 Something that just caught my eye, not excatly sur
darin (slow to review) 2017/02/14 19:50:05 isReady() checks mNativeAppWebMessagePort. I will
209 break;
210 }
222 } 211 }
223 if (mMessageCallback == null) {
224 Log.w(TAG,
225 "No handler set for port [" + mPortId + "], dropping mes sage " + message);
226 return;
227 }
228 mMessageCallback.onMessage(message, ports);
229 } 212 }
230 } 213 }
231 214
232 @Override 215 @Override
233 public void postMessage(String message, MessagePort[] sentPorts) throws Ille galStateException { 216 public void postMessage(String message, MessagePort[] sentPorts) throws Ille galStateException {
234 if (isClosed() || isTransferred()) { 217 if (isClosed() || isTransferred()) {
235 throw new IllegalStateException("Port is already closed or transferr ed"); 218 throw new IllegalStateException("Port is already closed or transferr ed");
236 } 219 }
237 AppWebMessagePort[] ports = null; 220 AppWebMessagePort[] ports = null;
238 if (sentPorts != null) { 221 if (sentPorts != null) {
239 for (MessagePort port : sentPorts) { 222 for (MessagePort port : sentPorts) {
240 if (port.equals(this)) { 223 if (port.equals(this)) {
241 throw new IllegalStateException("Source port cannot be trans ferred"); 224 throw new IllegalStateException("Source port cannot be trans ferred");
242 } 225 }
226 if (port.isClosed() || port.isTransferred()) {
227 throw new IllegalStateException("Port is already closed or t ransferred");
228 }
229 if (port.isStarted()) {
230 throw new IllegalStateException("Port is already started");
231 }
243 } 232 }
244 ports = Arrays.copyOf(sentPorts, sentPorts.length, AppWebMessagePort [].class); 233 ports = Arrays.copyOf(sentPorts, sentPorts.length, AppWebMessagePort [].class);
245 } 234 }
246 mStarted = true; 235 mStarted = true;
247 mPostMessageSender.postMessage(null, message, null, ports); 236 nativePostMessage(mNativeAppWebMessagePort, message, ports);
248 } 237 }
249 238
250 // Implements PostMessageSender.PostMessageSenderDelegate interface method. 239 private static native void nativeInitializeAppWebMessagePortPair(AppWebMessa gePort[] ports);
251 @Override
252 public boolean isPostMessageSenderReady() {
253 return isReady();
254 }
255 240
256 // Implements PostMessageSender.PostMessageSenderDelegate interface method. 241 private native void nativeCloseMessagePort(long nativeAppWebMessagePort);
257 @Override 242 private native void nativePostMessage(long nativeAppWebMessagePort, String m essage,
258 public void onPostMessageQueueEmpty() { 243 AppWebMessagePort[] ports);
259 if (isClosed()) { 244 private native boolean nativeDispatchNextMessage(long nativeAppWebMessagePor t);
260 cleanup(); 245 private native void nativeStartReceivingMessages(long nativeAppWebMessagePor t);
261 }
262 }
263
264 // Implements PostMessageSender.PostMessageSenderDelegate interface method.
265 @Override
266 public void postMessageToWeb(
267 String frameName, String message, String targetOrigin, int[] sentPor tIds) {
268 mMessagePortService.postMessage(mPortId, message, sentPortIds);
269 }
270
271 private void cleanup() {
272 mMessagePortService.removeObserver(mPostMessageSender);
273 mPostMessageSender = null;
274 mMessagePortService.closePort(mPortId);
275 }
276 } 246 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698