summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsgurun <sgurun@chromium.org>2015-02-26 10:50:05 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-26 18:52:46 +0000
commit1f47db6eeea87febb755db2c498335fd9bb226b1 (patch)
tree0db5b47a4f754b6cd30903ef58c815a5dca5e04b
parentd74986e2bed72beae3cb8b0bfc5f0fe73062000e (diff)
downloadchromium_src-1f47db6eeea87febb755db2c498335fd9bb226b1.zip
chromium_src-1f47db6eeea87febb755db2c498335fd9bb226b1.tar.gz
chromium_src-1f47db6eeea87febb755db2c498335fd9bb226b1.tar.bz2
Implement the close() API for Message ports
The close API has to consider these cases: 1. A message port can be in a pending state 2. A message could be queued in renderer waiting for conversion Therefore close() immediately closes the port, but it does not cleanup the resources until all the messages are transferred. BUG=393291 Review URL: https://codereview.chromium.org/956763002 Cr-Commit-Position: refs/heads/master@{#318271}
-rw-r--r--android_webview/browser/aw_message_port_message_filter.cc12
-rw-r--r--android_webview/browser/aw_message_port_message_filter.h3
-rw-r--r--android_webview/browser/aw_message_port_service.h2
-rw-r--r--android_webview/common/aw_message_port_messages.h10
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwContents.java15
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java17
-rw-r--r--android_webview/java/src/org/chromium/android_webview/MessagePort.java72
-rw-r--r--android_webview/java/src/org/chromium/android_webview/PostMessageSender.java26
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java170
-rw-r--r--android_webview/native/aw_message_port_service_impl.cc24
-rw-r--r--android_webview/native/aw_message_port_service_impl.h3
-rw-r--r--android_webview/renderer/aw_message_port_client.cc6
-rw-r--r--android_webview/renderer/aw_message_port_client.h1
-rw-r--r--content/browser/message_port_provider.cc7
-rw-r--r--content/public/browser/message_port_provider.h3
15 files changed, 294 insertions, 77 deletions
diff --git a/android_webview/browser/aw_message_port_message_filter.cc b/android_webview/browser/aw_message_port_message_filter.cc
index 60b143e..fc2d314 100644
--- a/android_webview/browser/aw_message_port_message_filter.cc
+++ b/android_webview/browser/aw_message_port_message_filter.cc
@@ -35,6 +35,7 @@ bool AwMessagePortMessageFilter::OnMessageReceived(
AwMessagePortService::OnConvertedWebToAppMessage)
IPC_MESSAGE_HANDLER(AwMessagePortHostMsg_ConvertedAppToWebMessage,
OnConvertedAppToWebMessage)
+ IPC_MESSAGE_HANDLER(AwMessagePortHostMsg_ClosePortAck, OnClosePortAck)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -44,10 +45,17 @@ void AwMessagePortMessageFilter::OnConvertedAppToWebMessage(
int msg_port_id,
const base::string16& message,
const std::vector<int>& sent_message_port_ids) {
+
MessagePortProvider::PostMessageToPort(msg_port_id, message,
sent_message_port_ids);
}
+void AwMessagePortMessageFilter::OnClosePortAck(int message_port_id) {
+ MessagePortProvider::ClosePort(message_port_id);
+ AwBrowserContext::GetDefault()->GetMessagePortService()->
+ CleanupPort(message_port_id);
+}
+
void AwMessagePortMessageFilter::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
@@ -62,6 +70,10 @@ void AwMessagePortMessageFilter::SendAppToWebMessage(
message, sent_message_port_ids));
}
+void AwMessagePortMessageFilter::SendClosePortMessage(int message_port_id) {
+ Send(new AwMessagePortMsg_ClosePort(route_id_, message_port_id));
+}
+
void AwMessagePortMessageFilter::SendMessage(
int msg_port_route_id,
const base::string16& message,
diff --git a/android_webview/browser/aw_message_port_message_filter.h b/android_webview/browser/aw_message_port_message_filter.h
index 5e0b9a0..2845068 100644
--- a/android_webview/browser/aw_message_port_message_filter.h
+++ b/android_webview/browser/aw_message_port_message_filter.h
@@ -26,13 +26,13 @@ class AwMessagePortMessageFilter : public content::BrowserMessageFilter,
void SendAppToWebMessage(int msg_port_route_id,
const base::string16& message,
const std::vector<int>& sent_message_port_ids);
+ void SendClosePortMessage(int message_port_id);
// MessagePortDelegate implementation.
void SendMessage(int msg_port_route_id,
const base::string16& message,
const std::vector<int>& sent_message_port_ids) override;
void SendMessagesAreQueued(int route_id) override;
-
private:
friend class content::BrowserThread;
friend class base::DeleteHelper<AwMessagePortMessageFilter>;
@@ -41,6 +41,7 @@ class AwMessagePortMessageFilter : public content::BrowserMessageFilter,
int msg_port_id,
const base::string16& message,
const std::vector<int>& sent_message_port_ids);
+ void OnClosePortAck(int message_port_id);
~AwMessagePortMessageFilter() override;
diff --git a/android_webview/browser/aw_message_port_service.h b/android_webview/browser/aw_message_port_service.h
index 62f46f0..779d90c 100644
--- a/android_webview/browser/aw_message_port_service.h
+++ b/android_webview/browser/aw_message_port_service.h
@@ -25,6 +25,8 @@ class AwMessagePortService {
virtual void OnMessagePortMessageFilterClosing(
AwMessagePortMessageFilter* filter) = 0;
+
+ virtual void CleanupPort(int message_port_id) = 0;
};
} // namespace android_webview
diff --git a/android_webview/common/aw_message_port_messages.h b/android_webview/common/aw_message_port_messages.h
index 00ec171..f1b0bf3 100644
--- a/android_webview/common/aw_message_port_messages.h
+++ b/android_webview/common/aw_message_port_messages.h
@@ -58,6 +58,12 @@ IPC_MESSAGE_ROUTED3(AwMessagePortMsg_AppToWebMessage,
base::string16 /* message */,
std::vector<int> /* sent message port_ids */)
+// Used to defer message port closing until after all in-flight messages
+// are flushed from renderer to browser. Renderer piggy-backs the message
+// to browser.
+IPC_MESSAGE_ROUTED1(AwMessagePortMsg_ClosePort,
+ int /* message port id */)
+
//-----------------------------------------------------------------------------
// These are messages sent from the renderer to the browser process.
@@ -72,3 +78,7 @@ IPC_MESSAGE_ROUTED3(AwMessagePortHostMsg_ConvertedAppToWebMessage,
int /* recipient message port id */,
base::string16 /* converted message */,
std::vector<int> /* sent message port_ids */)
+
+// Response to AwMessagePortMsg_ClosePort
+IPC_MESSAGE_ROUTED1(AwMessagePortHostMsg_ClosePortAck,
+ int /* message port id */)
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index e53f4f7..c95137c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1809,10 +1809,17 @@ public class AwContents implements SmartClipProvider,
sentPorts);
}
- /**
- * Posts a message to the destination frame for real. The unique message port
- * ids of any transferred port should be known at this time.
- */
+ // Implements PostMessageSender.PostMessageSenderDelegate interface method.
+ @Override
+ public boolean isPostMessageSenderReady() {
+ return true;
+ }
+
+ // Implements PostMessageSender.PostMessageSenderDelegate interface method.
+ @Override
+ public void onPostMessageQueueEmpty() { }
+
+ // Implements PostMessageSender.PostMessageSenderDelegate interface method.
@Override
public void postMessageToWeb(String frameName, String message, String targetOrigin,
int[] sentPortIds) {
diff --git a/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java b/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java
index 8d2c499..ddc09a0 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwMessagePortService.java
@@ -92,6 +92,12 @@ public class AwMessagePortService {
private SparseArray<MessagePort> mMessagePorts = new SparseArray<MessagePort>();
private Object mLock = new Object();
+ public void remove(int portId) {
+ synchronized (mLock) {
+ mMessagePorts.remove(portId);
+ }
+ }
+
public void put(int portId, MessagePort m) {
synchronized (mLock) {
mMessagePorts.put(portId, m);
@@ -123,12 +129,17 @@ public class AwMessagePortService {
mObserverList.removeObserver(observer);
}
+ public void closePort(int messagePortId) {
+ mPortStorage.remove(messagePortId);
+ if (mNativeMessagePortService == 0) return;
+ nativeClosePort(mNativeMessagePortService, messagePortId);
+ }
+
public void postMessage(int senderId, String message, int[] sentPorts) {
// verify that webview still owns the port (not transferred)
if (mPortStorage.get(senderId) == null) {
throw new IllegalStateException("Cannot post to unknown port " + senderId);
}
- removeSentPorts(sentPorts);
if (mNativeMessagePortService == 0) return;
nativePostAppToWebMessage(mNativeMessagePortService, senderId, message, sentPorts);
}
@@ -141,7 +152,7 @@ public class AwMessagePortService {
if (p == null) {
throw new IllegalStateException("Cannot transfer unknown port " + port);
}
- mPortStorage.put(port, null);
+ mPortStorage.remove(port);
}
}
}
@@ -190,4 +201,6 @@ public class AwMessagePortService {
private native long nativeInitAwMessagePortService();
private native void nativePostAppToWebMessage(long nativeAwMessagePortServiceImpl,
int senderId, String message, int[] portIds);
+ private native void nativeClosePort(long nativeAwMessagePortServiceImpl,
+ int messagePortId);
}
diff --git a/android_webview/java/src/org/chromium/android_webview/MessagePort.java b/android_webview/java/src/org/chromium/android_webview/MessagePort.java
index 2d0e393..620cbbb 100644
--- a/android_webview/java/src/org/chromium/android_webview/MessagePort.java
+++ b/android_webview/java/src/org/chromium/android_webview/MessagePort.java
@@ -22,17 +22,17 @@ import android.util.Log;
* 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 should be closed by the app when it is not needed any more. This will free
- * any resources used by it. A closed port cannot receive/send messages and cannot be transferred.
- * close() can be called multiple times.
- *
* 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.
*
+ * A message port should be closed by the app when it is not needed any more. This will free
+ * any resources used by it. A closed port cannot receive/send messages and cannot be transferred.
+ * close() can be called multiple times. A transferred port cannot be closed by the application,
+ * since the ownership is also transferred during the transfer. Closing a transferred port will
+ * throw an exception.
+ *
* TODO(sgurun) implement queueing messages while a port is in transfer
- * TODO(sgurun) implement freeing resources in content/message_port_service when a port is
- * closed
*/
public class MessagePort implements PostMessageSender.PostMessageSenderDelegate {
@@ -43,23 +43,6 @@ public class MessagePort implements PostMessageSender.PostMessageSenderDelegate
public abstract void onMessage(String message);
};
- /**
- * A specialized PostMessageSender for message channel message port.
- */
- private static class MessagePortPostMessageSender extends PostMessageSender {
-
- private MessagePort mSender;
-
- public MessagePortPostMessageSender(MessagePort sender, AwMessagePortService service) {
- super(sender, service);
- mSender = sender;
- }
- @Override
- protected boolean senderIsReady() {
- return mSender.isReady();
- }
- }
-
private static final String TAG = "MessagePort";
private static final int PENDING = -1;
private int mPortId = PENDING;
@@ -67,11 +50,11 @@ public class MessagePort implements PostMessageSender.PostMessageSenderDelegate
private AwMessagePortService mMessagePortService;
private boolean mClosed;
private boolean mTransferred;
- private MessagePortPostMessageSender mPostMessageSender;
+ private PostMessageSender mPostMessageSender;
public MessagePort(AwMessagePortService messagePortService) {
mMessagePortService = messagePortService;
- mPostMessageSender = new MessagePortPostMessageSender(this, mMessagePortService);
+ mPostMessageSender = new PostMessageSender(this, mMessagePortService);
mMessagePortService.addObserver(mPostMessageSender);
}
@@ -88,11 +71,17 @@ public class MessagePort implements PostMessageSender.PostMessageSenderDelegate
}
public void close() {
- if (!mClosed) {
- mClosed = true;
- mMessagePortService.removeObserver(mPostMessageSender);
- // TODO(sgurun) remove the port from AwMessagePortService and write a test
- // to verify it.
+ if (mTransferred) {
+ throw new IllegalStateException("Port is already transferred");
+ }
+ if (mClosed) return;
+ mClosed = true;
+ // 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();
}
}
@@ -130,7 +119,7 @@ public class MessagePort implements PostMessageSender.PostMessageSenderDelegate
}
if (msgPorts != null) {
for (MessagePort port : msgPorts) {
- if (port.portId() == mPortId) {
+ if (port.equals(this)) {
throw new IllegalStateException("Source port cannot be transferred");
}
}
@@ -138,9 +127,30 @@ public class MessagePort implements PostMessageSender.PostMessageSenderDelegate
mPostMessageSender.postMessage(null, message, null, msgPorts);
}
+ // Implements PostMessageSender.PostMessageSenderDelegate interface method.
+ @Override
+ public boolean isPostMessageSenderReady() {
+ return isReady();
+ }
+
+ // 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);
+ }
}
diff --git a/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java b/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java
index 7951838..01d7cae 100644
--- a/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java
+++ b/android_webview/java/src/org/chromium/android_webview/PostMessageSender.java
@@ -22,6 +22,16 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
*/
void postMessageToWeb(String frameName, String message, String targetOrigin,
int[] sentPortIds);
+
+ /*
+ * Whether the post message sender is ready to post messages.
+ */
+ boolean isPostMessageSenderReady();
+
+ /*
+ * Informs that all messages are posted and message queue is empty.
+ */
+ void onPostMessageQueueEmpty();
};
// A struct to store Message parameters that are sent from App to Web.
@@ -54,6 +64,12 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
mService = service;
}
+ // TODO(sgurun) in code review it was found this was implemented wrongly
+ // as mMessageQueue.size() > 0. write a test to catch this.
+ public boolean isMessageQueueEmpty() {
+ return mMessageQueue.size() == 0;
+ }
+
// Return true if any sent port is pending.
private boolean anySentPortIsPending(MessagePort[] sentPorts) {
if (sentPorts != null) {
@@ -66,11 +82,6 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
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
@@ -80,7 +91,7 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
private boolean shouldQueueMessage(MessagePort[] sentPorts) {
// if messages to frames are already in queue mode, simply queue it, no need to
// check ports.
- if (mMessageQueue.size() > 0 || !senderIsReady()) {
+ if (mMessageQueue.size() > 0 || !mDelegate.isPostMessageSenderReady()) {
return true;
}
if (anySentPortIsPending(sentPorts)) {
@@ -131,7 +142,7 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
public void onMessageChannelCreated() {
PostMessageParams msg;
- if (!senderIsReady()) {
+ if (!mDelegate.isPostMessageSenderReady()) {
return;
}
@@ -143,5 +154,6 @@ public class PostMessageSender implements AwMessagePortService.MessageChannelObs
mMessageQueue.remove();
postMessageToWeb(msg.frameName, msg.message, msg.targetOrigin, msg.sentPorts);
}
+ mDelegate.onPostMessageQueueEmpty();
}
}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
index 8849ec9..a9923be 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -122,6 +122,52 @@ public class PostMessageTest extends AwTestBase {
+ " </script>"
+ "</body></html>";
+ // Concats all the data fields of the received messages and makes it
+ // available as page title.
+ private static final String TITLE_FROM_POSTMESSAGE_TO_FRAME =
+ "<!DOCTYPE html><html><body>"
+ + " <script>"
+ + " var received = '';"
+ + " onmessage = function (e) {"
+ + " received += e.data;"
+ + " document.title = received;"
+ + " }"
+ + " </script>"
+ + "</body></html>";
+
+ // Concats all the data fields of the received messages to the transferred channel
+ // and makes it available as page title.
+ private static final String TITLE_FROM_POSTMESSAGE_TO_CHANNEL =
+ "<!DOCTYPE html><html><body>"
+ + " <script>"
+ + " var received = '';"
+ + " onmessage = function (e) {"
+ + " var myport = e.ports[0];"
+ + " myport.onmessage = function (f) {"
+ + " received = received + f.data;"
+ + " document.title = received;"
+ + " }"
+ + " }"
+ + " </script>"
+ + "</body></html>";
+
+ // Call on non-UI thread.
+ private void expectTitle(final String title) throws Throwable {
+ assertTrue("Received title " + mAwContents.getTitle() + " while expecting " + title,
+ CriteriaHelper.pollForCriteria(new Criteria() {
+ @Override
+ public boolean isSatisfied() {
+ return ThreadUtils.runOnUiThreadBlockingNoException(
+ new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return mAwContents.getTitle().equals(title);
+ }
+ });
+ }
+ }));
+ }
+
private void loadPage(String page) throws Throwable {
final String url = mWebServer.setResponse("/test.html", page,
CommonResources.getTextHtmlHeaders(true));
@@ -271,6 +317,96 @@ public class PostMessageTest extends AwTestBase {
boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
}
+ // Verify messages posted before closing a port is received at the destination port.
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-PostMessage"})
+ public void testMessagesPostedBeforeClosingPortAreTransferred() throws Throwable {
+ loadPage(TITLE_FROM_POSTMESSAGE_TO_CHANNEL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MessagePort[] channel = mAwContents.createMessageChannel();
+ mAwContents.postMessageToFrame(null, "1", mWebServer.getBaseUrl(),
+ new MessagePort[]{channel[1]});
+ channel[0].postMessage("2", null);
+ channel[0].postMessage("3", null);
+ channel[0].close();
+ }
+ });
+ expectTitle("23");
+ }
+
+ // Verify a transferred port using postmessagetoframe cannot be closed.
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-PostMessage"})
+ public void testClosingTransferredPortToFrameThrowsAnException() throws Throwable {
+ loadPage(TEST_PAGE);
+ final CountDownLatch latch = new CountDownLatch(1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MessagePort[] channel = mAwContents.createMessageChannel();
+ mAwContents.postMessageToFrame(null, "1", mWebServer.getBaseUrl(),
+ new MessagePort[]{channel[1]});
+ try {
+ channel[1].close();
+ } catch (IllegalStateException ex) {
+ latch.countDown();
+ return;
+ }
+ fail();
+ }
+ });
+ boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+
+ // Verify a transferred port using postmessagetoframe cannot be closed.
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-PostMessage"})
+ public void testClosingTransferredPortToChannelThrowsAnException() throws Throwable {
+ loadPage(TEST_PAGE);
+ final CountDownLatch latch = new CountDownLatch(1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MessagePort[] channel1 = mAwContents.createMessageChannel();
+ mAwContents.postMessageToFrame(null, "1", mWebServer.getBaseUrl(),
+ new MessagePort[]{channel1[1]});
+ MessagePort[] channel2 = mAwContents.createMessageChannel();
+ channel1[0].postMessage("2", new MessagePort[]{channel2[0]});
+ try {
+ channel2[0].close();
+ } catch (IllegalStateException ex) {
+ latch.countDown();
+ return;
+ }
+ fail();
+ }
+ });
+ boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+
+ // Create two message channels, and while they are in pending state, transfer the
+ // second one in the first one.
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-PostMessage"})
+ public void testPendingPortCanBeTransferredInPendingPort() throws Throwable {
+ loadPage(TITLE_FROM_POSTMESSAGE_TO_CHANNEL);
+ final TestMessagePort testPort =
+ new TestMessagePort(getAwBrowserContext().getMessagePortService());
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MessagePort[] channel1 = mAwContents.createMessageChannel();
+ mAwContents.postMessageToFrame(null, "1", mWebServer.getBaseUrl(),
+ new MessagePort[]{channel1[1]});
+ MessagePort[] channel2 = mAwContents.createMessageChannel();
+ channel1[0].postMessage("2", new MessagePort[]{channel2[0]});
+ }
+ });
+ expectTitle("2");
+ }
+
private static class ChannelContainer {
private boolean mReady;
private MessagePort[] mChannel;
@@ -475,42 +611,12 @@ public class PostMessageTest extends AwTestBase {
assertEquals(HELLO, channelContainer.getMessage());
}
- // concats all the data fields of the received messages and makes it
- // available as page title.
- private static final String TITLE_PAGE =
- "<!DOCTYPE html><html><body>"
- + " <script>"
- + " var received = \"\";"
- + " onmessage = function (e) {"
- + " received = received + e.data;"
- + " document.title = received;"
- + " }"
- + " </script>"
- + "</body></html>";
-
- // Call on non-UI thread.
- private void expectTitle(final String title) throws Throwable {
- assertTrue("Received title " + mAwContents.getTitle() + " while expecting " + title,
- CriteriaHelper.pollForCriteria(new Criteria() {
- @Override
- public boolean isSatisfied() {
- return ThreadUtils.runOnUiThreadBlockingNoException(
- new Callable<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- return mAwContents.getTitle().equals(title);
- }
- });
- }
- }));
- }
-
// Post a message with a pending port to a frame and then post a bunch of messages
// after that. Make sure that they are not ordered at the receiver side.
@SmallTest
@Feature({"AndroidWebView", "Android-PostMessage"})
public void testPostMessageToFrameNotReordersMessages() throws Throwable {
- loadPage(TITLE_PAGE);
+ loadPage(TITLE_FROM_POSTMESSAGE_TO_FRAME);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
@@ -586,7 +692,7 @@ public class PostMessageTest extends AwTestBase {
@SmallTest
@Feature({"AndroidWebView", "Android-PostMessage"})
public void testPostMessageToFrameNotSendsPendingMessages() throws Throwable {
- loadPage(TITLE_PAGE);
+ loadPage(TITLE_FROM_POSTMESSAGE_TO_FRAME);
final TestMessagePort testPort =
new TestMessagePort(getAwBrowserContext().getMessagePortService());
runTestOnUiThread(new Runnable() {
diff --git a/android_webview/native/aw_message_port_service_impl.cc b/android_webview/native/aw_message_port_service_impl.cc
index d2e1fa4..b4bf2ca 100644
--- a/android_webview/native/aw_message_port_service_impl.cc
+++ b/android_webview/native/aw_message_port_service_impl.cc
@@ -135,6 +135,21 @@ void AwMessagePortServiceImpl::PostAppToWebMessage(JNIEnv* env, jobject obj,
base::Owned(j_sent_ports)));
}
+// The message port service cannot immediately close the port, because
+// it is possible that messages are still queued in the renderer process
+// waiting for a conversion. Instead, it sends a special message with
+// a flag which indicates that this message port should be closed.
+void AwMessagePortServiceImpl::ClosePort(JNIEnv* env, jobject obj,
+ int message_port_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&AwMessagePortServiceImpl::PostClosePortMessage,
+ base::Unretained(this),
+ message_port_id));
+}
+
void AwMessagePortServiceImpl::RemoveSentPorts(
const std::vector<int>& sent_ports) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -151,6 +166,15 @@ void AwMessagePortServiceImpl::PostAppToWebMessageOnIOThread(
ports_[sender_id]->SendAppToWebMessage(sender_id, *message, *sent_ports);
}
+void AwMessagePortServiceImpl::PostClosePortMessage(int message_port_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ ports_[message_port_id]->SendClosePortMessage(message_port_id);
+}
+
+void AwMessagePortServiceImpl::CleanupPort(int message_port_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ ports_.erase(message_port_id);
+}
void AwMessagePortServiceImpl::CreateMessageChannelOnIOThread(
scoped_refptr<AwMessagePortMessageFilter> filter,
diff --git a/android_webview/native/aw_message_port_service_impl.h b/android_webview/native/aw_message_port_service_impl.h
index 3ab5b88..fcb83be 100644
--- a/android_webview/native/aw_message_port_service_impl.h
+++ b/android_webview/native/aw_message_port_service_impl.h
@@ -39,10 +39,12 @@ class AwMessagePortServiceImpl : public AwMessagePortService {
const std::vector<int>& sent_message_port_ids) override;
void OnMessagePortMessageFilterClosing(
AwMessagePortMessageFilter* filter) override;
+ void CleanupPort(int message_port_id) override;
// Methods called from Java.
void PostAppToWebMessage(JNIEnv* env, jobject object, int sender_id,
jstring message, jintArray sent_ports);
+ void ClosePort(JNIEnv* env, jobject object, int message_port_id);
void RemoveSentPorts(const std::vector<int>& sent_ports);
@@ -60,6 +62,7 @@ private:
int* port1,
int* port2);
void AddPort(int message_port_id, AwMessagePortMessageFilter* filter);
+ void PostClosePortMessage(int message_port_id);
JavaObjectWeakGlobalRef java_ref_;
typedef std::map<int, AwMessagePortMessageFilter*> MessagePorts;
diff --git a/android_webview/renderer/aw_message_port_client.cc b/android_webview/renderer/aw_message_port_client.cc
index 7a5f67e..54edf28 100644
--- a/android_webview/renderer/aw_message_port_client.cc
+++ b/android_webview/renderer/aw_message_port_client.cc
@@ -34,6 +34,7 @@ bool AwMessagePortClient::OnMessageReceived(
IPC_BEGIN_MESSAGE_MAP(AwMessagePortClient, message)
IPC_MESSAGE_HANDLER(AwMessagePortMsg_WebToAppMessage, OnWebToAppMessage)
IPC_MESSAGE_HANDLER(AwMessagePortMsg_AppToWebMessage, OnAppToWebMessage)
+ IPC_MESSAGE_HANDLER(AwMessagePortMsg_ClosePort, OnClosePort)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -95,4 +96,9 @@ void AwMessagePortClient::OnAppToWebMessage(
result, sent_message_port_ids));
}
+void AwMessagePortClient::OnClosePort(int message_port_id) {
+ Send(new AwMessagePortHostMsg_ClosePortAck(render_frame()->GetRoutingID(),
+ message_port_id));
+}
+
}
diff --git a/android_webview/renderer/aw_message_port_client.h b/android_webview/renderer/aw_message_port_client.h
index bd21472..a8b5253 100644
--- a/android_webview/renderer/aw_message_port_client.h
+++ b/android_webview/renderer/aw_message_port_client.h
@@ -30,6 +30,7 @@ class AwMessagePortClient : public content::RenderFrameObserver {
void OnAppToWebMessage(int message_port_id,
const base::string16& message,
const std::vector<int>& sent_message_port_ids);
+ void OnClosePort(int message_port_id);
DISALLOW_COPY_AND_ASSIGN(AwMessagePortClient);
};
diff --git a/content/browser/message_port_provider.cc b/content/browser/message_port_provider.cc
index fac53a9..ac329bc 100644
--- a/content/browser/message_port_provider.cc
+++ b/content/browser/message_port_provider.cc
@@ -90,6 +90,13 @@ void MessagePortProvider::PostMessageToPort(
}
// static
+void MessagePortProvider::ClosePort(int message_port_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ MessagePortService* msp = MessagePortService::GetInstance();
+ msp->ClosePort(message_port_id);
+}
+
+// static
void MessagePortProvider::OnMessagePortDelegateClosing(
MessagePortDelegate* delegate) {
MessagePortService::GetInstance()->OnMessagePortDelegateClosing(delegate);
diff --git a/content/public/browser/message_port_provider.h b/content/public/browser/message_port_provider.h
index 1042399..8a725de 100644
--- a/content/public/browser/message_port_provider.h
+++ b/content/public/browser/message_port_provider.h
@@ -47,6 +47,9 @@ class CONTENT_EXPORT MessagePortProvider {
const base::string16& data,
const std::vector<int>& sent_ports);
+ // Close the message port. Should be called on IO thread.
+ static void ClosePort(int message_port_id);
+
// Cleanup the message ports that belong to the closing delegate.
static void OnMessagePortDelegateClosing(MessagePortDelegate * delegate);