Chromium Code Reviews| Index: android_webview/java/src/org/chromium/android_webview/PostMessageSender.java |
| diff --git a/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java b/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1159c1bb6e3a2c86ad72a5397ee4d97350c58d5a |
| --- /dev/null |
| +++ b/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java |
| @@ -0,0 +1,149 @@ |
| +// 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 java.util.ArrayDeque; |
|
hush (inactive)
2015/02/13 03:25:08
please add a new line after this.
sgurun-gerrit only
2015/02/13 05:11:59
Done.
|
| +/** |
| + * Sanity checks and sends post messages to web. Queues messages if necessary. |
| + */ |
| +public class PostMessageSender implements AwMessagePortService.MessageChannelObserver { |
| + |
| + /** |
| + * The interface for message handler. |
| + */ |
| + public static interface PostMessageSenderDelegate { |
| + void postMessageToWeb(String frameName, String message, String sourceOrigin, |
| + String targetOrigin, int[] sentPorts); |
| + }; |
| + |
| + // A struct to store Message parameters that are sent from App to Web. |
| + private static class PostMessageParams { |
| + public String frameName; |
| + public String message; |
| + public String sourceOrigin; |
| + public String targetOrigin; |
| + public MessagePort[] sentPorts; |
| + |
| + public PostMessageParams(String frameName, String message, String sourceOrigin, |
| + String targetOrigin, MessagePort[] sentPorts) { |
| + this.frameName = frameName; |
| + this.message = message; |
| + this.sourceOrigin = sourceOrigin; |
| + this.targetOrigin = targetOrigin; |
| + this.sentPorts = sentPorts; |
| + } |
| + } |
| + |
| + private PostMessageSenderDelegate mDelegate; |
| + private AwMessagePortService mService; |
| + |
| + // If a message that is sent from android webview to web has a pending port (a port that |
| + // is not internally created yet), this message, and any following messages will be |
| + // queued until the transferred port becomes ready. This is necessary to prevent any |
| + // reordering. |
| + private boolean mShouldQueue = false; |
| + private ArrayDeque<PostMessageParams> mMessageQueue = new ArrayDeque<PostMessageParams>(); |
| + |
| + public PostMessageSender(PostMessageSenderDelegate delegate, AwMessagePortService service) { |
| + mDelegate = delegate; |
| + mService = service; |
| + } |
| + |
| + // Return true if any sent port is pending. |
| + private boolean anySentPortIsPending(MessagePort[] sentPorts) { |
| + if (sentPorts != null) { |
| + for (MessagePort port : sentPorts) { |
| + if (!port.isReady()) { |
| + return true; |
| + } |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + // By default the sender is always ready. |
| + protected boolean senderIsReady() { |
| + return true; |
| + } |
| + |
| + // A message to a frame is queued if: |
| + // 1. Sender is not ready to post. When posting messages to frames, sender is always |
| + // ready. However, when posting messages using message channels, sender may be in |
| + // a pending state. |
| + // 2. The mShouldQueue mode is already on. Having mShouldQueue mode true means |
| + // there is a already a message with a pending port in queue. |
| + // 3. The message includes a port that is not ready yet. |
| + private boolean shouldQueueMessage(MessagePort[] sentPorts) { |
| + // if messages to frames are already in queue mode, simply queue it, no need to |
| + // check ports. |
| + if (mShouldQueue || !senderIsReady()) { |
| + return true; |
| + } |
| + if (anySentPortIsPending(sentPorts)) { |
| + mShouldQueue = true; |
|
hush (inactive)
2015/02/13 03:25:08
I feel it is weird that a method that looks like i
sgurun-gerrit only
2015/02/13 05:11:59
this is a private method so I did not worry too mu
|
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + private void postMessageToWeb(String frameName, String message, String sourceOrigin, |
| + String targetOrigin, MessagePort[] sentPorts) { |
| + int[] portIds = null; |
| + if (sentPorts != null) { |
| + portIds = new int[sentPorts.length]; |
| + for (int i = 0; i < sentPorts.length; i++) { |
| + portIds[i] = sentPorts[i].portId(); |
| + } |
| + mService.removeSentPorts(portIds); |
| + } |
| + mDelegate.postMessageToWeb(frameName, message, sourceOrigin, targetOrigin, portIds); |
| + } |
| + |
| + /* |
| + * Sanity checks the message and queues it if necessary. Posts the message to delegate |
| + * when message can be sent. |
| + */ |
| + public void postMessage(String frameName, String message, String sourceOrigin, |
|
hush (inactive)
2015/02/13 03:25:08
name it postMessageIfPossible?
sgurun-gerrit only
2015/02/13 05:11:59
I think the name is compatible with HTML5 MessageP
|
| + String targetOrigin, MessagePort[] sentPorts) throws IllegalStateException { |
| + // Sanity check all the ports that are being transferred. |
| + if (sentPorts != null) { |
| + for (MessagePort p : sentPorts) { |
| + if (p.isClosed() || p.isTransferred()) { |
| + throw new IllegalStateException("Port cannot be transferred"); |
| + } |
| + p.setTransferred(); |
| + } |
| + } |
| + if (shouldQueueMessage(sentPorts)) { |
| + mMessageQueue.add(new PostMessageParams(frameName, message, sourceOrigin, |
| + targetOrigin, sentPorts)); |
| + } else { |
| + postMessageToWeb(frameName, message, sourceOrigin, targetOrigin, sentPorts); |
| + } |
| + } |
| + |
| + /* |
| + * Starts posting any messages that are queued. |
| + */ |
| + public void onMessageChannelCreated() { |
| + PostMessageParams msg; |
| + |
| + if (!senderIsReady()) { |
| + return; |
| + } |
| + |
| + while ((msg = mMessageQueue.peek()) != null) { |
| + // If there are still pending ports, message cannot be posted. |
| + if (anySentPortIsPending(msg.sentPorts)) { |
| + return; |
| + } |
| + mMessageQueue.remove(); |
| + postMessageToWeb(msg.frameName, msg.message, msg.sourceOrigin, msg.targetOrigin, |
| + msg.sentPorts); |
| + } |
| + // No more messages in the queue. |
| + mShouldQueue = false; |
| + } |
| +} |