OLD | NEW |
---|---|
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 * |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
66 * A port is put to a "started" state if: | 68 * A port is put to a "started" state if: |
67 * 1. The port is ever used to post a message, or | 69 * 1. The port is ever used to post a message, or |
68 * 2. The port was ever registered a handler to receive a message. | 70 * 2. The port was ever registered a handler to receive a message. |
69 * A started port cannot be transferred. | 71 * A started port cannot be transferred. |
70 * | 72 * |
71 * This restriction should not impact postmessage functionality in a big way, | 73 * 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 | 74 * 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 | 75 * transferring data. As a return, it simplifies implementation and prevents har d |
74 * to debug, racy corner cases while receiving/sending data. | 76 * to debug, racy corner cases while receiving/sending data. |
75 */ | 77 */ |
76 public class AppWebMessagePort implements MessagePort, PostMessageSender.PostMes sageSenderDelegate { | 78 @JNINamespace("content") |
77 private static final String TAG = "MessagePort"; | 79 public class AppWebMessagePort implements MessagePort { |
78 private static final int PENDING = -1; | 80 private static final String TAG = "AppWebMessagePort"; |
81 private static final long UNINITIALIZED_PORT_NATIVE_PTR = 0; | |
79 | 82 |
80 // the what value for POST_MESSAGE | 83 // The |what| value for handleMessage. |
81 private static final int POST_MESSAGE = 1; | 84 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 | 85 |
96 // Implements the handler to handle messageport messages received from web. | 86 // Implements the handler to handle messageport messages received from web. |
97 // These messages are received on IO thread and normally handled in main | 87 // 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. | 88 // thread however, alternatively application can pass a handler to execute t hem. |
99 private static class MessageHandler extends Handler { | 89 private static class MessageHandler extends Handler { |
100 public MessageHandler(Looper looper) { | 90 public MessageHandler(Looper looper) { |
101 super(looper); | 91 super(looper); |
102 } | 92 } |
103 @Override | 93 @Override |
104 public void handleMessage(Message msg) { | 94 public void handleMessage(Message msg) { |
105 if (msg.what == POST_MESSAGE) { | 95 if (msg.what == MESSAGES_AVAILABLE) { |
106 PostMessageFromWeb m = (PostMessageFromWeb) msg.obj; | 96 AppWebMessagePort port = (AppWebMessagePort) msg.obj; |
107 m.port.onMessage(m.message, m.sentPorts); | 97 port.dispatchReceivedMessages(); |
108 return; | 98 return; |
109 } | 99 } |
110 throw new IllegalStateException("undefined message"); | 100 throw new IllegalStateException("undefined message"); |
111 } | 101 } |
112 } | 102 } |
113 // The default message handler | 103 // The default message handler |
114 private static final MessageHandler sDefaultHandler = | 104 private static final MessageHandler sDefaultHandler = |
115 new MessageHandler(Looper.getMainLooper()); | 105 new MessageHandler(Looper.getMainLooper()); |
116 | 106 |
117 private int mPortId = PENDING; | 107 private long mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR; |
108 | |
118 private MessageCallback mMessageCallback; | 109 private MessageCallback mMessageCallback; |
119 private AppWebMessagePortService mMessagePortService; | |
120 private boolean mClosed; | 110 private boolean mClosed; |
121 private boolean mTransferred; | 111 private boolean mTransferred; |
122 private boolean mStarted; | 112 private boolean mStarted; |
123 private boolean mReleasedMessages; | |
124 private PostMessageSender mPostMessageSender; | |
125 private MessageHandler mHandler; | 113 private MessageHandler mHandler; |
126 private final Object mLock = new Object(); | 114 private final Object mLock = new Object(); |
127 | 115 |
Yusuf
2017/02/07 19:12:17
javadoc?
darin (slow to review)
2017/02/08 07:29:27
Done.
| |
128 public AppWebMessagePort(AppWebMessagePortService messagePortService) { | 116 public static AppWebMessagePort[] createPair() { |
129 mMessagePortService = messagePortService; | 117 AppWebMessagePort[] ports = |
130 mPostMessageSender = new PostMessageSender(this, mMessagePortService); | 118 new AppWebMessagePort[] { new AppWebMessagePort(), new AppWebMessage Port() }; |
131 mMessagePortService.addObserver(mPostMessageSender); | 119 ports[0].nativeInitializeAppWebMessagePortPair(ports); |
Yusuf
2017/02/07 19:12:17
We can declare this static on this side as well. S
darin (slow to review)
2017/02/08 07:29:27
Thanks, this worked. I had previously marked the m
| |
120 return ports; | |
121 } | |
122 | |
Yusuf
2017/02/07 19:12:17
We don't need this anymore?
darin (slow to review)
2017/02/08 07:29:27
Good catch. Removed.
| |
123 public AppWebMessagePort() { | |
132 } | 124 } |
133 | 125 |
134 @Override | 126 @Override |
135 public boolean isReady() { | 127 public boolean isReady() { |
136 return mPortId != PENDING; | 128 return mNativeAppWebMessagePort != UNINITIALIZED_PORT_NATIVE_PTR; |
137 } | 129 } |
138 | 130 |
139 public int portId() { | 131 @CalledByNative |
140 return mPortId; | 132 private void setNativeAppWebMessagePort(long nativeAppWebMessagePort) { |
133 mNativeAppWebMessagePort = nativeAppWebMessagePort; | |
141 } | 134 } |
142 | 135 |
143 public void setPortId(int id) { | 136 @CalledByNative |
144 mPortId = id; | 137 private long releaseNativePortForTransfer() { |
145 releaseMessages(); | 138 mTransferred = true; |
139 long port = mNativeAppWebMessagePort; | |
140 mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR; | |
141 return port; | |
146 } | 142 } |
147 | 143 |
148 @Override | 144 @Override |
149 public void close() { | 145 public void close() { |
150 if (mTransferred) { | 146 if (mTransferred) { |
151 throw new IllegalStateException("Port is already transferred"); | 147 throw new IllegalStateException("Port is already transferred"); |
152 } | 148 } |
153 synchronized (mLock) { | 149 synchronized (mLock) { |
154 if (mClosed) return; | 150 if (mClosed) return; |
155 mClosed = true; | 151 mClosed = true; |
156 } | 152 } |
157 // If the port is already ready, and no messages are waiting in the | 153 if (isReady()) { |
158 // queue to be transferred, onPostMessageQueueEmpty() callback is not | |
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(); | 154 cleanup(); |
163 } | 155 } |
164 } | 156 } |
165 | 157 |
166 @Override | 158 @Override |
167 public boolean isClosed() { | 159 public boolean isClosed() { |
168 return mClosed; | 160 synchronized (mLock) { |
161 return mClosed; | |
162 } | |
169 } | 163 } |
170 | 164 |
171 @Override | 165 @Override |
172 public boolean isTransferred() { | 166 public boolean isTransferred() { |
173 return mTransferred; | 167 return mTransferred; |
174 } | 168 } |
175 | 169 |
176 public void setTransferred() { | |
177 mTransferred = true; | |
178 } | |
179 | |
180 @Override | 170 @Override |
181 public boolean isStarted() { | 171 public boolean isStarted() { |
182 return mStarted; | 172 return mStarted; |
183 } | 173 } |
184 | 174 |
185 // Only called on UI thread | 175 // Only called on UI thread |
186 @Override | 176 @Override |
187 public void setMessageCallback(MessageCallback messageCallback, Handler hand ler) { | 177 public void setMessageCallback(MessageCallback messageCallback, Handler hand ler) { |
188 mStarted = true; | 178 mStarted = true; |
189 synchronized (mLock) { | 179 synchronized (mLock) { |
190 mMessageCallback = messageCallback; | 180 mMessageCallback = messageCallback; |
191 if (handler != null) { | 181 if (handler != null) { |
192 mHandler = new MessageHandler(handler.getLooper()); | 182 mHandler = new MessageHandler(handler.getLooper()); |
193 } | 183 } |
194 } | 184 } |
195 releaseMessages(); | 185 nativeStartReceivingMessages(mNativeAppWebMessagePort); |
196 } | 186 } |
197 | 187 |
198 // Only called on IO thread. | 188 // Called on a background thread. |
199 public void onReceivedMessage(String message, AppWebMessagePort[] sentPorts) { | 189 @CalledByNative |
190 private void onMessagesAvailable() { | |
200 synchronized (mLock) { | 191 synchronized (mLock) { |
201 PostMessageFromWeb m = new PostMessageFromWeb(this, message, sentPor ts); | |
202 Handler handler = mHandler != null ? mHandler : sDefaultHandler; | 192 Handler handler = mHandler != null ? mHandler : sDefaultHandler; |
203 Message msg = handler.obtainMessage(POST_MESSAGE, m); | 193 Message msg = handler.obtainMessage(MESSAGES_AVAILABLE, this); |
204 handler.sendMessage(msg); | 194 handler.sendMessage(msg); |
205 } | 195 } |
206 } | 196 } |
207 | 197 |
208 private void releaseMessages() { | |
209 if (mReleasedMessages || !isReady() || mMessageCallback == null) { | |
210 return; | |
211 } | |
212 mReleasedMessages = true; | |
213 mMessagePortService.releaseMessages(mPortId); | |
214 } | |
215 | |
216 // This method may be called on a different thread than UI thread. | 198 // This method may be called on a different thread than UI thread. |
217 public void onMessage(String message, AppWebMessagePort[] ports) { | 199 @CalledByNative |
200 private void onReceivedMessage(String message, AppWebMessagePort[] ports) { | |
218 synchronized (mLock) { | 201 synchronized (mLock) { |
219 if (isClosed()) { | 202 if (isClosed()) { |
220 Log.w(TAG, "Port [" + mPortId + "] received message in closed st ate"); | 203 Log.w(TAG, "Port [" + mNativeAppWebMessagePort |
204 + "] received message in closed state"); | |
221 return; | 205 return; |
222 } | 206 } |
223 if (mMessageCallback == null) { | 207 if (mMessageCallback == null) { |
224 Log.w(TAG, | 208 Log.w(TAG, "No handler set for port [" + mNativeAppWebMessagePor t |
225 "No handler set for port [" + mPortId + "], dropping mes sage " + message); | 209 + "], dropping message " + message); |
226 return; | 210 return; |
227 } | 211 } |
228 mMessageCallback.onMessage(message, ports); | 212 mMessageCallback.onMessage(message, ports); |
229 } | 213 } |
230 } | 214 } |
231 | 215 |
216 public void dispatchReceivedMessages() { | |
Yusuf
2017/02/07 19:12:17
javadoc
darin (slow to review)
2017/02/08 07:29:27
I just changed this method to be private.
| |
217 nativeDispatchReceivedMessages(mNativeAppWebMessagePort); | |
218 } | |
219 | |
232 @Override | 220 @Override |
233 public void postMessage(String message, MessagePort[] sentPorts) throws Ille galStateException { | 221 public void postMessage(String message, MessagePort[] sentPorts) throws Ille galStateException { |
234 if (isClosed() || isTransferred()) { | 222 if (isClosed() || isTransferred()) { |
235 throw new IllegalStateException("Port is already closed or transferr ed"); | 223 throw new IllegalStateException("Port is already closed or transferr ed"); |
236 } | 224 } |
237 AppWebMessagePort[] ports = null; | 225 AppWebMessagePort[] ports = null; |
238 if (sentPorts != null) { | 226 if (sentPorts != null) { |
239 for (MessagePort port : sentPorts) { | 227 for (MessagePort port : sentPorts) { |
240 if (port.equals(this)) { | 228 if (port.equals(this)) { |
241 throw new IllegalStateException("Source port cannot be trans ferred"); | 229 throw new IllegalStateException("Source port cannot be trans ferred"); |
242 } | 230 } |
231 if (port.isClosed() || port.isTransferred()) { | |
232 throw new IllegalStateException("Port is already closed or t ransferred"); | |
233 } | |
234 if (port.isStarted()) { | |
235 throw new IllegalStateException("Port is already started"); | |
236 } | |
243 } | 237 } |
244 ports = Arrays.copyOf(sentPorts, sentPorts.length, AppWebMessagePort [].class); | 238 ports = Arrays.copyOf(sentPorts, sentPorts.length, AppWebMessagePort [].class); |
245 } | 239 } |
246 mStarted = true; | 240 mStarted = true; |
247 mPostMessageSender.postMessage(null, message, null, ports); | 241 nativePostMessage(mNativeAppWebMessagePort, message, ports); |
248 } | |
249 | |
250 // Implements PostMessageSender.PostMessageSenderDelegate interface method. | |
251 @Override | |
252 public boolean isPostMessageSenderReady() { | |
253 return isReady(); | |
254 } | |
255 | |
256 // Implements PostMessageSender.PostMessageSenderDelegate interface method. | |
257 @Override | |
258 public void onPostMessageQueueEmpty() { | |
259 if (isClosed()) { | |
260 cleanup(); | |
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 } | 242 } |
270 | 243 |
271 private void cleanup() { | 244 private void cleanup() { |
272 mMessagePortService.removeObserver(mPostMessageSender); | 245 nativeCloseMessagePort(mNativeAppWebMessagePort); |
273 mPostMessageSender = null; | 246 mNativeAppWebMessagePort = UNINITIALIZED_PORT_NATIVE_PTR; |
274 mMessagePortService.closePort(mPortId); | |
275 } | 247 } |
248 | |
249 private native void nativeInitializeAppWebMessagePortPair(AppWebMessagePort[ ] ports); | |
Yusuf
2017/02/07 19:12:17
you can declare this private static native and the
| |
250 | |
251 private native void nativeCloseMessagePort(long nativeAppWebMessagePort); | |
252 private native void nativePostMessage(long nativeAppWebMessagePort, String m essage, | |
253 AppWebMessagePort[] ports); | |
254 private native void nativeDispatchReceivedMessages(long nativeAppWebMessageP ort); | |
255 private native void nativeStartReceivingMessages(long nativeAppWebMessagePor t); | |
276 } | 256 } |
OLD | NEW |