OLD | NEW |
| (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.content.browser; | |
6 | |
7 import java.util.ArrayDeque; | |
8 | |
9 /** | |
10 * Sanity checks and sends post messages to web. Queues messages if necessary. | |
11 */ | |
12 public class PostMessageSender implements AppWebMessagePortService.MessageChanne
lObserver { | |
13 /** | |
14 * The interface for message handler. | |
15 */ | |
16 public static interface PostMessageSenderDelegate { | |
17 /* | |
18 * Posts a message to the destination frame for real. The unique message
port | |
19 * id of any transferred port should be known at this time. | |
20 */ | |
21 void postMessageToWeb( | |
22 String frameName, String message, String targetOrigin, int[] sen
tPortIds); | |
23 | |
24 /* | |
25 * Whether the post message sender is ready to post messages. | |
26 */ | |
27 boolean isPostMessageSenderReady(); | |
28 | |
29 /* | |
30 * Informs that all messages are posted and message queue is empty. | |
31 */ | |
32 void onPostMessageQueueEmpty(); | |
33 } | |
34 ; | |
35 | |
36 // A struct to store Message parameters that are sent from App to Web. | |
37 private static class PostMessageParams { | |
38 public String frameName; | |
39 public String message; | |
40 public String targetOrigin; | |
41 public AppWebMessagePort[] sentPorts; | |
42 | |
43 public PostMessageParams(String frameName, String message, String target
Origin, | |
44 AppWebMessagePort[] sentPorts) { | |
45 this.frameName = frameName; | |
46 this.message = message; | |
47 this.targetOrigin = targetOrigin; | |
48 this.sentPorts = sentPorts; | |
49 } | |
50 } | |
51 | |
52 private PostMessageSenderDelegate mDelegate; | |
53 private AppWebMessagePortService mService; | |
54 | |
55 // If a message that is sent from android webview to web has a pending port
(a port that | |
56 // is not internally created yet), this message, and any following messages
will be | |
57 // queued until the transferred port becomes ready. This is necessary to pre
vent any | |
58 // reordering. | |
59 private ArrayDeque<PostMessageParams> mMessageQueue = new ArrayDeque<PostMes
sageParams>(); | |
60 | |
61 public PostMessageSender(PostMessageSenderDelegate delegate, AppWebMessagePo
rtService service) { | |
62 mDelegate = delegate; | |
63 mService = service; | |
64 } | |
65 | |
66 // TODO(sgurun) in code review it was found this was implemented wrongly | |
67 // as mMessageQueue.size() > 0. write a test to catch this. | |
68 public boolean isMessageQueueEmpty() { | |
69 return mMessageQueue.size() == 0; | |
70 } | |
71 | |
72 // Return true if any sent port is pending. | |
73 private boolean anySentPortIsPending(AppWebMessagePort[] sentPorts) { | |
74 if (sentPorts != null) { | |
75 for (AppWebMessagePort port : sentPorts) { | |
76 if (!port.isReady()) { | |
77 return true; | |
78 } | |
79 } | |
80 } | |
81 return false; | |
82 } | |
83 | |
84 // A message to a frame is queued if: | |
85 // 1. Sender is not ready to post. When posting messages to frames, sender i
s always | |
86 // ready. However, when posting messages using message channels, sender may
be in | |
87 // a pending state. | |
88 // 2. There are already queued messages | |
89 // 3. The message includes a port that is not ready yet. | |
90 private boolean shouldQueueMessage(AppWebMessagePort[] sentPorts) { | |
91 // if messages to frames are already in queue mode, simply queue it, no
need to | |
92 // check ports. | |
93 if (mMessageQueue.size() > 0 || !mDelegate.isPostMessageSenderReady()) { | |
94 return true; | |
95 } | |
96 if (anySentPortIsPending(sentPorts)) { | |
97 return true; | |
98 } | |
99 return false; | |
100 } | |
101 | |
102 private void postMessageToWeb( | |
103 String frameName, String message, String targetOrigin, AppWebMessage
Port[] sentPorts) { | |
104 int[] portIds = null; | |
105 if (sentPorts != null) { | |
106 portIds = new int[sentPorts.length]; | |
107 for (int i = 0; i < sentPorts.length; i++) { | |
108 portIds[i] = sentPorts[i].portId(); | |
109 } | |
110 mService.removeSentPorts(portIds); | |
111 } | |
112 mDelegate.postMessageToWeb(frameName, message, targetOrigin, portIds); | |
113 } | |
114 | |
115 /* | |
116 * Sanity checks the message and queues it if necessary. Posts the message t
o delegate | |
117 * when message can be sent. | |
118 */ | |
119 public void postMessage(String frameName, String message, String targetOrigi
n, | |
120 AppWebMessagePort[] sentPorts) throws IllegalStateException { | |
121 // Sanity check all the ports that are being transferred. | |
122 if (sentPorts != null) { | |
123 for (AppWebMessagePort p : sentPorts) { | |
124 if (p.isClosed()) { | |
125 throw new IllegalStateException("Closed port cannot be trans
fered"); | |
126 } | |
127 if (p.isTransferred()) { | |
128 throw new IllegalStateException("Port cannot be re-transferr
ed"); | |
129 } | |
130 if (p.isStarted()) { | |
131 throw new IllegalStateException("Started port cannot be tran
sferred"); | |
132 } | |
133 p.setTransferred(); | |
134 } | |
135 } | |
136 if (shouldQueueMessage(sentPorts)) { | |
137 mMessageQueue.add(new PostMessageParams(frameName, message, targetOr
igin, sentPorts)); | |
138 } else { | |
139 postMessageToWeb(frameName, message, targetOrigin, sentPorts); | |
140 } | |
141 } | |
142 | |
143 /* | |
144 * Starts posting any messages that are queued. | |
145 */ | |
146 public void onMessageChannelCreated() { | |
147 PostMessageParams msg; | |
148 | |
149 if (!mDelegate.isPostMessageSenderReady()) { | |
150 return; | |
151 } | |
152 | |
153 while ((msg = mMessageQueue.peek()) != null) { | |
154 // If there are still pending ports, message cannot be posted. | |
155 if (anySentPortIsPending(msg.sentPorts)) { | |
156 return; | |
157 } | |
158 mMessageQueue.remove(); | |
159 postMessageToWeb(msg.frameName, msg.message, msg.targetOrigin, msg.s
entPorts); | |
160 } | |
161 mDelegate.onPostMessageQueueEmpty(); | |
162 } | |
163 } | |
OLD | NEW |