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

Side by Side Diff: android_webview/java/src/org/chromium/android_webview/AwMessagePort.java

Issue 2375133002: Move MessagePort implementation from android_webview to content (Closed)
Patch Set: Created 4 years, 2 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
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.android_webview;
6
7 import android.os.Handler;
8 import android.os.Looper;
9 import android.os.Message;
10 import android.util.Log;
11
12 /**
13 * Represents the MessageChannel MessagePort object. Inspired from
14 * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.htm l#message-channels
15 *
16 * State management:
17 *
18 * Initially a message port will be in a pending state. It will be ready once it is created in
19 * content/ (in IO thread) and a message port id is assigned.
20 * A pending message port cannnot be transferred, and cannot send or receive mes sages. However,
21 * these details are hidden from the user. If a message port is in the pending s tate:
22 * 1. Any messages posted in this port will be queued until the port is ready
23 * 2. Transferring the port using a message channel will cause the message (and any subsequent
24 * messages sent) to be queued until the port is ready
25 * 3. Transferring the pending port via postMessageToFrame will cause the messag e (and all
26 * subsequent messages posted via postMessageToFrame) to be queued until the port is ready.
27 *
28 * A message port can be in transferred state while a transfer is pending or com plete. An
29 * application cannot use a transferred port to post messages. If a transferred port
30 * receives messages, they will be queued. This state is not visible to embedder app.
31 *
32 * A message port should be closed by the app when it is not needed any more. Th is will free
33 * any resources used by it. A closed port cannot receive/send messages and cann ot be transferred.
34 * close() can be called multiple times. A transferred port cannot be closed by the application,
35 * since the ownership is also transferred during the transfer. Closing a transf erred port will
36 * throw an exception.
37 *
38 * The fact that messages can be handled on a separate thread means that thread
39 * synchronization is important. All methods are called on UI thread except as n oted.
40 *
41 * Restrictions:
42 * The HTML5 message protocol is very flexible in transferring ports. However, t his
43 * sometimes leads to surprising behavior. For example, in current version of ch rome (m41)
44 * the code below
45 * 1. var c1 = new MessageChannel();
46 * 2. var c2 = new MessageChannel();
47 * 3. c1.port2.onmessage= function(e) { console.log("1"); }
48 * 4. c2.port2.onmessage = function(e) {
49 * 5. e.ports[0].onmessage = function(f) {
50 * 6. console.log("3");
51 * 7. }
52 * 8. }
53 * 9. c1.port1.postMessage("test");
54 * 10. c2.port1.postMessage("test2",[c1.port2])
55 *
56 * prints 1 or 3 depending on whether or not line 10 is included in code. Furthe r if
57 * it gets executed with a timeout, depending on timeout value, the printout val ue
58 * changes.
59 *
60 * To prevent such problems, Android webview implementation limits the transfer of ports
61 * as below:
62 * Webview puts a port to a "started" state if:
63 * 1. The port is ever used to post a message, or
64 * 2. The port was ever registered a handler to receive a message.
65 * A started port cannot be transferred.
66 *
67 * This restriction should not impact postmessage functionality in a big way,
68 * because an app can still create as many channels as it wants to and use it fo r
69 * transferring data. As a return, it simplifies implementation and prevents har d
70 * to debug, racy corner cases while receiving/sending data.
71 */
72 public class AwMessagePort implements PostMessageSender.PostMessageSenderDelegat e {
73
74 /**
75 * The message callback for receiving messages. Called on UI thread or if
76 * provided, on the handler that is provided.
77 */
78 public abstract static class MessageCallback {
79 public abstract void onMessage(String message, AwMessagePort[] sentPorts );
80 }
81
82 private static final String TAG = "MessagePort";
83 private static final int PENDING = -1;
84
85 // the what value for POST_MESSAGE
86 private static final int POST_MESSAGE = 1;
87
88 private static class PostMessageFromWeb {
89 public AwMessagePort port;
90 public String message;
91 public AwMessagePort[] sentPorts;
92
93 public PostMessageFromWeb(AwMessagePort port, String message, AwMessageP ort[] sentPorts) {
94 this.port = port;
95 this.message = message;
96 this.sentPorts = sentPorts;
97 }
98 }
99
100 // Implements the handler to handle messageport messages received from web.
101 // These messages are received on IO thread and normally handled in main
102 // thread however, alternatively application can pass a handler to execute t hem.
103 private static class MessageHandler extends Handler {
104 public MessageHandler(Looper looper) {
105 super(looper);
106 }
107 @Override
108 public void handleMessage(Message msg) {
109 if (msg.what == POST_MESSAGE) {
110 PostMessageFromWeb m = (PostMessageFromWeb) msg.obj;
111 m.port.onMessage(m.message, m.sentPorts);
112 return;
113 }
114 throw new IllegalStateException("undefined message");
115 }
116 }
117 // The default message handler
118 private static final MessageHandler sDefaultHandler =
119 new MessageHandler(Looper.getMainLooper());
120
121 private int mPortId = PENDING;
122 private MessageCallback mMessageCallback;
123 private AwMessagePortService mMessagePortService;
124 private boolean mClosed;
125 private boolean mTransferred;
126 private boolean mStarted;
127 private boolean mReleasedMessages;
128 private PostMessageSender mPostMessageSender;
129 private MessageHandler mHandler;
130 private final Object mLock = new Object();
131
132 public AwMessagePort(AwMessagePortService messagePortService) {
133 mMessagePortService = messagePortService;
134 mPostMessageSender = new PostMessageSender(this, mMessagePortService);
135 mMessagePortService.addObserver(mPostMessageSender);
136 }
137
138 public boolean isReady() {
139 return mPortId != PENDING;
140 }
141
142 public int portId() {
143 return mPortId;
144 }
145
146 public void setPortId(int id) {
147 mPortId = id;
148 releaseMessages();
149 }
150
151 public void close() {
152 if (mTransferred) {
153 throw new IllegalStateException("Port is already transferred");
154 }
155 synchronized (mLock) {
156 if (mClosed) return;
157 mClosed = true;
158 }
159 // If the port is already ready, and no messages are waiting in the
160 // queue to be transferred, onPostMessageQueueEmpty() callback is not
161 // received (it is received only after messages are purged). In this
162 // case do the cleanup here.
163 if (isReady() && mPostMessageSender.isMessageQueueEmpty()) {
164 cleanup();
165 }
166 }
167
168 public boolean isClosed() {
169 return mClosed;
170 }
171
172 public boolean isTransferred() {
173 return mTransferred;
174 }
175
176 public void setTransferred() {
177 mTransferred = true;
178 }
179
180 public boolean isStarted() {
181 return mStarted;
182 }
183
184 // Only called on UI thread
185 public void setMessageCallback(MessageCallback messageCallback, Handler hand ler) {
186 mStarted = true;
187 synchronized (mLock) {
188 mMessageCallback = messageCallback;
189 if (handler != null) {
190 mHandler = new MessageHandler(handler.getLooper());
191 }
192 }
193 releaseMessages();
194 }
195
196 // Only called on IO thread.
197 public void onReceivedMessage(String message, AwMessagePort[] sentPorts) {
198 synchronized (mLock) {
199 PostMessageFromWeb m = new PostMessageFromWeb(this, message, sentPor ts);
200 Handler handler = mHandler != null ? mHandler : sDefaultHandler;
201 Message msg = handler.obtainMessage(POST_MESSAGE, m);
202 handler.sendMessage(msg);
203 }
204 }
205
206 private void releaseMessages() {
207 if (mReleasedMessages || !isReady() || mMessageCallback == null) {
208 return;
209 }
210 mReleasedMessages = true;
211 mMessagePortService.releaseMessages(mPortId);
212 }
213
214 // This method may be called on a different thread than UI thread.
215 public void onMessage(String message, AwMessagePort[] ports) {
216 synchronized (mLock) {
217 if (isClosed()) {
218 Log.w(TAG, "Port [" + mPortId + "] received message in closed st ate");
219 return;
220 }
221 if (mMessageCallback == null) {
222 Log.w(TAG, "No handler set for port [" + mPortId + "], dropping message "
223 + message);
224 return;
225 }
226 mMessageCallback.onMessage(message, ports);
227 }
228 }
229
230 public void postMessage(String message, AwMessagePort[] sentPorts)
231 throws IllegalStateException {
232 if (isClosed() || isTransferred()) {
233 throw new IllegalStateException("Port is already closed or transferr ed");
234 }
235 if (sentPorts != null) {
236 for (AwMessagePort port : sentPorts) {
237 if (port.equals(this)) {
238 throw new IllegalStateException("Source port cannot be trans ferred");
239 }
240 }
241 }
242 mStarted = true;
243 mPostMessageSender.postMessage(null, message, null, sentPorts);
244 }
245
246 // Implements PostMessageSender.PostMessageSenderDelegate interface method.
247 @Override
248 public boolean isPostMessageSenderReady() {
249 return isReady();
250 }
251
252 // Implements PostMessageSender.PostMessageSenderDelegate interface method.
253 @Override
254 public void onPostMessageQueueEmpty() {
255 if (isClosed()) {
256 cleanup();
257 }
258 }
259
260 // Implements PostMessageSender.PostMessageSenderDelegate interface method.
261 @Override
262 public void postMessageToWeb(String frameName, String message, String target Origin,
263 int[] sentPortIds) {
264 mMessagePortService.postMessage(mPortId, message, sentPortIds);
265 }
266
267 private void cleanup() {
268 mMessagePortService.removeObserver(mPostMessageSender);
269 mPostMessageSender = null;
270 mMessagePortService.closePort(mPortId);
271 }
272 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698