1 // Copyright 2014 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.test;
6 
7 import android.graphics.Rect;
8 import android.net.Uri;
9 import android.support.test.InstrumentationRegistry;
10 import android.webkit.JavascriptInterface;
11 
12 import androidx.test.filters.SmallTest;
13 
14 import org.hamcrest.Matchers;
15 import org.junit.After;
16 import org.junit.Assert;
17 import org.junit.Before;
18 import org.junit.Rule;
19 import org.junit.Test;
20 import org.junit.runner.RunWith;
21 
22 import org.chromium.android_webview.AwContents;
23 import org.chromium.android_webview.JsReplyProxy;
24 import org.chromium.android_webview.WebMessageListener;
25 import org.chromium.android_webview.test.AwActivityTestRule.PopupInfo;
26 import org.chromium.android_webview.test.TestAwContentsClient.ShouldInterceptRequestHelper;
27 import org.chromium.android_webview.test.util.CommonResources;
28 import org.chromium.base.ThreadUtils;
29 import org.chromium.base.test.util.Criteria;
30 import org.chromium.base.test.util.CriteriaHelper;
31 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
32 import org.chromium.base.test.util.Feature;
33 import org.chromium.content_public.browser.MessagePort;
34 import org.chromium.content_public.browser.SelectionPopupController;
35 import org.chromium.content_public.browser.test.util.DOMUtils;
36 import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
37 import org.chromium.content_public.browser.test.util.TestThreadUtils;
38 import org.chromium.net.test.util.TestWebServer;
39 
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.concurrent.LinkedBlockingQueue;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * Tests for pop up window flow.
47  */
48 @RunWith(AwJUnit4ClassRunner.class)
49 public class PopupWindowTest {
50     @Rule
51     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
52 
53     private TestAwContentsClient mParentContentsClient;
54     private AwTestContainerView mParentContainerView;
55     private AwContents mParentContents;
56     private TestWebServer mWebServer;
57 
58     private static final String POPUP_TITLE = "Popup Window";
59 
60     @Before
setUp()61     public void setUp() throws Exception {
62         mParentContentsClient = new TestAwContentsClient();
63         mParentContainerView =
64                 mActivityTestRule.createAwTestContainerViewOnMainSync(mParentContentsClient);
65         mParentContents = mParentContainerView.getAwContents();
66         mWebServer = TestWebServer.start();
67     }
68 
69     @After
tearDown()70     public void tearDown() {
71         if (mWebServer != null) {
72             mWebServer.shutdown();
73         }
74     }
75 
76     @Test
77     @SmallTest
78     @Feature({"AndroidWebView"})
testPopupWindow()79     public void testPopupWindow() throws Throwable {
80         final String popupPath = "/popup.html";
81         final String parentPageHtml = CommonResources.makeHtmlPageFrom("", "<script>"
82                         + "function tryOpenWindow() {"
83                         + "  var newWindow = window.open('" + popupPath + "');"
84                         + "}</script>");
85 
86         final String popupPageHtml = CommonResources.makeHtmlPageFrom(
87                 "<title>" + POPUP_TITLE + "</title>",
88                 "This is a popup window");
89 
90         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
91                 parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
92         AwContents popupContents =
93                 mActivityTestRule.connectPendingPopup(mParentContents).popupContents;
94         Assert.assertEquals(POPUP_TITLE, mActivityTestRule.getTitleOnUiThread(popupContents));
95     }
96 
97     @Test
98     @SmallTest
99     @Feature({"AndroidWebView"})
testJavascriptInterfaceForPopupWindow()100     public void testJavascriptInterfaceForPopupWindow() throws Throwable {
101         // android.webkit.cts.WebViewTest#testJavascriptInterfaceForClientPopup
102         final String popupPath = "/popup.html";
103         final String parentPageHtml = CommonResources.makeHtmlPageFrom("",
104                 "<script>"
105                         + "function tryOpenWindow() {"
106                         + "  var newWindow = window.open('" + popupPath + "');"
107                         + "}</script>");
108 
109         final String popupPageHtml = CommonResources.makeHtmlPageFrom(
110                 "<title>" + POPUP_TITLE + "</title>", "This is a popup window");
111 
112         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
113                 parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
114         PopupInfo popupInfo = mActivityTestRule.createPopupContents(mParentContents);
115         TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient;
116         final AwContents popupContents = popupInfo.popupContents;
117 
118         class DummyJavaScriptInterface {
119             @JavascriptInterface
120             public int test() {
121                 return 42;
122             }
123         }
124         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
125 
126         AwActivityTestRule.addJavascriptInterfaceOnUiThread(popupContents, obj, "dummy");
127 
128         mActivityTestRule.loadPopupContents(mParentContents, popupInfo, null);
129 
130         AwActivityTestRule.pollInstrumentationThread(() -> {
131             String ans = mActivityTestRule.executeJavaScriptAndWaitForResult(
132                     popupContents, popupContentsClient, "dummy.test()");
133 
134             return ans.equals("42");
135         });
136     }
137 
138     @Test
139     @SmallTest
140     @Feature({"AndroidWebView"})
testDefaultUserAgentsInParentAndChildWindows()141     public void testDefaultUserAgentsInParentAndChildWindows() throws Throwable {
142         mActivityTestRule.getAwSettingsOnUiThread(mParentContents).setJavaScriptEnabled(true);
143         mActivityTestRule.loadUrlSync(
144                 mParentContents, mParentContentsClient.getOnPageFinishedHelper(), "about:blank");
145         String parentUserAgent = mActivityTestRule.executeJavaScriptAndWaitForResult(
146                 mParentContents, mParentContentsClient, "navigator.userAgent");
147 
148         final String popupPath = "/popup.html";
149         final String myUserAgentString = "myUserAgent";
150         final String parentPageHtml = CommonResources.makeHtmlPageFrom("",
151                 "<script>"
152                         + "function tryOpenWindow() {"
153                         + "  var newWindow = window.open('" + popupPath + "');"
154                         + "}</script>");
155 
156         final String popupPageHtml = "<html><head><script>"
157                 + "document.title = navigator.userAgent;"
158                 + "</script><body><div id='a'></div></body></html>";
159 
160         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
161                 parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
162 
163         PopupInfo popupInfo = mActivityTestRule.createPopupContents(mParentContents);
164         TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient;
165         final AwContents popupContents = popupInfo.popupContents;
166 
167         mActivityTestRule.loadPopupContents(mParentContents, popupInfo, null);
168 
169         final String childUserAgent = mActivityTestRule.executeJavaScriptAndWaitForResult(
170                 popupContents, popupContentsClient, "navigator.userAgent");
171 
172         Assert.assertEquals(parentUserAgent, childUserAgent);
173     }
174 
175     @Test
176     @SmallTest
177     @Feature({"AndroidWebView"})
testOverrideUserAgentInOnCreateWindow()178     public void testOverrideUserAgentInOnCreateWindow() throws Throwable {
179         final String popupPath = "/popup.html";
180         final String myUserAgentString = "myUserAgent";
181         final String parentPageHtml = CommonResources.makeHtmlPageFrom("",
182                 "<script>"
183                         + "function tryOpenWindow() {"
184                         + "  var newWindow = window.open('" + popupPath + "');"
185                         + "}</script>");
186 
187         final String popupPageHtml = "<html><head><script>"
188                 + "document.title = navigator.userAgent;"
189                 + "</script><body><div id='a'></div></body></html>";
190 
191         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
192                 parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
193 
194         PopupInfo popupInfo = mActivityTestRule.createPopupContents(mParentContents);
195         TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient;
196         final AwContents popupContents = popupInfo.popupContents;
197 
198         // Override the user agent string for the popup window.
199         mActivityTestRule.loadPopupContents(
200                 mParentContents, popupInfo, new AwActivityTestRule.OnCreateWindowHandler() {
201                     @Override
202                     public boolean onCreateWindow(AwContents awContents) {
203                         awContents.getSettings().setUserAgentString(myUserAgentString);
204                         return true;
205                     }
206                 });
207 
208         CriteriaHelper.pollUiThread(() -> {
209             try {
210                 Criteria.checkThat(mActivityTestRule.getTitleOnUiThread(popupContents),
211                         Matchers.is(myUserAgentString));
212             } catch (Exception e) {
213                 throw new CriteriaNotSatisfiedException(e);
214             }
215         });
216     }
217 
218     @Test
219     @SmallTest
220     @Feature({"AndroidWebView"})
testOnPageFinishedCalledOnDomModificationAfterNavigation()221     public void testOnPageFinishedCalledOnDomModificationAfterNavigation() throws Throwable {
222         final String popupPath = "/popup.html";
223         final String parentPageHtml = CommonResources.makeHtmlPageFrom("",
224                 "<script>"
225                         + "function tryOpenWindow() {"
226                         + "  window.popupWindow = window.open('" + popupPath + "');"
227                         + "}"
228                         + "function modifyDomOfPopup() {"
229                         + "  window.popupWindow.document.body.innerHTML = 'Hello from the parent!';"
230                         + "}</script>");
231 
232         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
233                 parentPageHtml, "<html></html>", popupPath, "tryOpenWindow()");
234         PopupInfo popupInfo = mActivityTestRule.createPopupContents(mParentContents);
235         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
236                 popupInfo.popupContentsClient.getOnPageFinishedHelper();
237         ShouldInterceptRequestHelper shouldInterceptRequestHelper =
238                 popupInfo.popupContentsClient.getShouldInterceptRequestHelper();
239         int onPageFinishedCount = onPageFinishedHelper.getCallCount();
240         int shouldInterceptRequestCount = shouldInterceptRequestHelper.getCallCount();
241         // Modify DOM before navigation gets committed. Once it gets committed, then
242         // DidAccessInitialDocument does not get triggered.
243         popupInfo.popupContentsClient.getShouldInterceptRequestHelper().runDuringFirstTimeCallback(
244                 () -> {
245                     ThreadUtils.assertOnBackgroundThread();
246                     try {
247                         // Ensures that we modify DOM before navigation gets committed.
248                         mActivityTestRule.executeJavaScriptAndWaitForResult(
249                                 mParentContents, mParentContentsClient, "modifyDomOfPopup()");
250                     } catch (Exception e) {
251                         throw new RuntimeException(e);
252                     }
253                 });
254         mActivityTestRule.loadPopupContents(mParentContents, popupInfo, null);
255         shouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCount);
256         // Modifying DOM in the middle while loading a popup window - this causes navigation state
257         // change through AwWebContentsDelegateAdapter#navigationStateChanged(), resulting in an
258         // additional onPageFinished() callback. Also, this eventually affects commit stage of the
259         // navigation which creates additional navigationStateChanged() and one additional
260         // onPageFinished() callback.
261         onPageFinishedHelper.waitForCallback(onPageFinishedCount, 4);
262         // This is the URL that gets shown to the user because parent changed DOM of the popup
263         // window.
264         List<String> urlList = onPageFinishedHelper.getUrlList();
265         Assert.assertEquals("about:blank", urlList.get(onPageFinishedCount));
266         // Note that in this test we do not stop the navigation and we still navigate to the page
267         // that we wanted. The loaded page does not have the changed DOM. This is slightly different
268         // from the original workflow in b/19325392 as there is no good hook to stop navigation and
269         // trigger DidAccessInitialDocument at the same time.
270         Assert.assertTrue(urlList.get(onPageFinishedCount + 2).endsWith(popupPath));
271         Assert.assertTrue(urlList.get(onPageFinishedCount + 3).endsWith(popupPath));
272     }
273 
274     @Test
275     @SmallTest
276     @Feature({"AndroidWebView"})
testPopupWindowTextHandle()277     public void testPopupWindowTextHandle() throws Throwable {
278         final String popupPath = "/popup.html";
279         final String parentPageHtml = CommonResources.makeHtmlPageFrom("", "<script>"
280                         + "function tryOpenWindow() {"
281                         + "  var newWindow = window.open('" + popupPath + "');"
282                         + "}</script>");
283 
284         final String popupPageHtml = CommonResources.makeHtmlPageFrom(
285                 "<title>" + POPUP_TITLE + "</title>",
286                 "<span id=\"plain_text\" class=\"full_view\">This is a popup window.</span>");
287 
288         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
289                 parentPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
290         PopupInfo popupInfo = mActivityTestRule.connectPendingPopup(mParentContents);
291         final AwContents popupContents = popupInfo.popupContents;
292         TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient;
293         Assert.assertEquals(POPUP_TITLE, mActivityTestRule.getTitleOnUiThread(popupContents));
294 
295         AwActivityTestRule.enableJavaScriptOnUiThread(popupContents);
296 
297         // Now long press on some texts and see if the text handles show up.
298         DOMUtils.longPressNode(popupContents.getWebContents(), "plain_text");
299         SelectionPopupController controller = TestThreadUtils.runOnUiThreadBlocking(
300                 () -> SelectionPopupController.fromWebContents(popupContents.getWebContents()));
301         assertWaitForSelectActionBarStatus(true, controller);
302         Assert.assertTrue(TestThreadUtils.runOnUiThreadBlocking(() -> controller.hasSelection()));
303 
304         // Now hide the select action bar. This should hide the text handles and
305         // clear the selection.
306         hideSelectActionMode(controller);
307 
308         assertWaitForSelectActionBarStatus(false, controller);
309         String jsGetSelection = "window.getSelection().toString()";
310         // Test window.getSelection() returns empty string "" literally.
311         Assert.assertEquals("\"\"",
312                 mActivityTestRule.executeJavaScriptAndWaitForResult(
313                         popupContents, popupContentsClient, jsGetSelection));
314     }
315 
316     @Test
317     @SmallTest
318     @Feature({"AndroidWebView"})
testPopupWindowHasUserGestureForUserInitiated()319     public void testPopupWindowHasUserGestureForUserInitiated() throws Throwable {
320         runPopupUserGestureTest(true);
321     }
322 
323     @Test
324     @SmallTest
325     @Feature({"AndroidWebView"})
testPopupWindowHasUserGestureForUserInitiatedNoOpener()326     public void testPopupWindowHasUserGestureForUserInitiatedNoOpener() throws Throwable {
327         runPopupUserGestureTest(false);
328     }
329 
runPopupUserGestureTest(boolean hasOpener)330     private void runPopupUserGestureTest(boolean hasOpener) throws Throwable {
331         TestThreadUtils.runOnUiThreadBlocking(() -> {
332             mParentContents.getSettings().setJavaScriptEnabled(true);
333             mParentContents.getSettings().setSupportMultipleWindows(true);
334             mParentContents.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
335         });
336 
337         final String body = String.format(Locale.US,
338                 "<a href=\"popup.html\" id=\"link\" %s target=\"_blank\">example.com</a>",
339                 hasOpener ? "" : "rel=\"noopener noreferrer\"");
340         final String mainHtml = CommonResources.makeHtmlPageFrom("", body);
341         final String openerUrl = mWebServer.setResponse("/popupOpener.html", mainHtml, null);
342         final String popupUrl = mWebServer.setResponse("/popup.html",
343                 CommonResources.makeHtmlPageFrom(
344                         "<title>" + POPUP_TITLE + "</title>", "This is a popup window"),
345                 null);
346 
347         mParentContentsClient.getOnCreateWindowHelper().setReturnValue(true);
348         mActivityTestRule.loadUrlSync(
349                 mParentContents, mParentContentsClient.getOnPageFinishedHelper(), openerUrl);
350 
351         TestAwContentsClient.OnCreateWindowHelper onCreateWindowHelper =
352                 mParentContentsClient.getOnCreateWindowHelper();
353         int currentCallCount = onCreateWindowHelper.getCallCount();
354         DOMUtils.clickNode(mParentContents.getWebContents(), "link");
355         onCreateWindowHelper.waitForCallback(
356                 currentCallCount, 1, AwActivityTestRule.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
357 
358         Assert.assertTrue(onCreateWindowHelper.getIsUserGesture());
359     }
360 
361     @Test
362     @SmallTest
363     @Feature({"AndroidWebView"})
testPopupWindowNoUserGestureForJsInitiated()364     public void testPopupWindowNoUserGestureForJsInitiated() throws Throwable {
365         final String popupPath = "/popup.html";
366         final String openerPageHtml = CommonResources.makeHtmlPageFrom("",
367                 "<script>"
368                         + "function tryOpenWindow() {"
369                         + "  var newWindow = window.open('" + popupPath + "');"
370                         + "}</script>");
371 
372         final String popupPageHtml = CommonResources.makeHtmlPageFrom(
373                 "<title>" + POPUP_TITLE + "</title>", "This is a popup window");
374 
375         mActivityTestRule.triggerPopup(mParentContents, mParentContentsClient, mWebServer,
376                 openerPageHtml, popupPageHtml, popupPath, "tryOpenWindow()");
377         TestAwContentsClient.OnCreateWindowHelper onCreateWindowHelper =
378                 mParentContentsClient.getOnCreateWindowHelper();
379         Assert.assertFalse(onCreateWindowHelper.getIsUserGesture());
380     }
381 
382     private static class TestWebMessageListener implements WebMessageListener {
383         private LinkedBlockingQueue<Data> mQueue = new LinkedBlockingQueue<>();
384 
385         public static class Data {
386             public String mMessage;
387             public boolean mIsMainFrame;
388             public JsReplyProxy mReplyProxy;
389 
Data(String message, boolean isMainFrame, JsReplyProxy replyProxy)390             public Data(String message, boolean isMainFrame, JsReplyProxy replyProxy) {
391                 mMessage = message;
392                 mIsMainFrame = isMainFrame;
393                 mReplyProxy = replyProxy;
394             }
395         }
396 
397         @Override
onPostMessage(String message, Uri sourceOrigin, boolean isMainFrame, JsReplyProxy replyProxy, MessagePort[] ports)398         public void onPostMessage(String message, Uri sourceOrigin, boolean isMainFrame,
399                 JsReplyProxy replyProxy, MessagePort[] ports) {
400             mQueue.add(new Data(message, isMainFrame, replyProxy));
401         }
402 
waitForOnPostMessage()403         public Data waitForOnPostMessage() throws Exception {
404             return AwActivityTestRule.waitForNextQueueElement(mQueue);
405         }
406     }
407 
408     // Regression test for crbug.com/1083819.
409     //
410     // The setup of this test is to have an iframe inside of a main frame, give the iframe user
411     // gesture, then window.open() on javascript: scheme. We are verifying that the
412     // JavaScript code isn't running in the main frame's context when
413     // getJavaScriptCanOpenWindowsAutomatically() is false.
414     //
415     // There are several steps in this test:
416     // 1. Load the web page (main.html), which has an cross-origin iframe (iframe.html).
417     // 2. main frame send a message to browser to establish the connection.
418     // 3. iframe send a message to browser to estiablish the connection and notify the location of
419     //    the |iframe_link| element.
420     // 4. Click the iframe_link element to give user gesture.
421     // 5. Browser waits until receives "clicked" message.
422     // 6. Browser asks the iframe to call window.open() and waits for the "done" message.
423     // 7. Browser asks the main frame to check if an element was injected and waits the result.
424     @Test
425     @SmallTest
426     @Feature({"AndroidWebView"})
testSingleWindowModeJsInjection()427     public void testSingleWindowModeJsInjection() throws Throwable {
428         // Choose a free port which is different from |mWebServer| so they have different origins.
429         TestWebServer crossOriginWebServer = TestWebServer.startAdditional();
430 
431         final String windowOpenJavaScript = "javascript:{"
432                 + "  let elem = document.createElement('p');"
433                 + "  elem.setAttribute('id', 'inject');"
434                 + "  document.body.append(elem);"
435                 + "}";
436         final String iframeHtml = "<html><head>"
437                 + "<script>"
438                 + "  myObject.onmessage = function(e) {"
439                 + "    window.open(\"" + windowOpenJavaScript + "\");"
440                 + "    myObject.postMessage('done');"
441                 + "  };"
442                 + "  window.onload = function() {"
443                 + "    let link = document.getElementById('iframe_link');"
444                 + "    let rect = link.getBoundingClientRect();"
445                 + "    let message = Math.round(rect.left) + ';' + Math.round(rect.top) + ';';"
446                 + "    message += Math.round(rect.right) + ';' + Math.round(rect.bottom);"
447                 + "    myObject.postMessage(message);"
448                 + "  };"
449                 + "</script>"
450                 + "</head><body>"
451                 + "  <div>I am iframe.</div>"
452                 + "  <a href='#' id='iframe_link' onclick='myObject.postMessage(\"clicked\");'>"
453                 + "   iframe link"
454                 + "  </a>"
455                 + "</body></html>";
456         final String iframeHtmlPath =
457                 crossOriginWebServer.setResponse("/iframe.html", iframeHtml, null);
458         final String mainHtml = "<html><head><script>"
459                 + "  myObject.onmessage = function(e) {"
460                 + "    let elem = document.getElementById('inject');"
461                 + "    if (elem) { myObject.postMessage('failed'); }"
462                 + "    else { myObject.postMessage('succeed'); }"
463                 + "  };"
464                 + "  myObject.postMessage('init');"
465                 + "</script></head><body>"
466                 + "<iframe src='" + iframeHtmlPath + "'></iframe>"
467                 + "<div>I am main frame</div>"
468                 + "</body></html>";
469         final String mainHtmlPath = mWebServer.setResponse("/main.html", mainHtml, null);
470 
471         TestWebMessageListener webMessageListener = new TestWebMessageListener();
472         TestThreadUtils.runOnUiThreadBlocking(() -> {
473             mParentContents.getSettings().setJavaScriptEnabled(true);
474             // |false| is the default setting for setSupportMultipleWindows(), we explicitly set it
475             // to |false| here for better readability.
476             mParentContents.getSettings().setSupportMultipleWindows(false);
477             mParentContents.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
478             mParentContents.addWebMessageListener(
479                     "myObject", new String[] {"*"}, webMessageListener);
480         });
481 
482         // Step 1.
483         mActivityTestRule.loadUrlSync(
484                 mParentContents, mParentContentsClient.getOnPageFinishedHelper(), mainHtmlPath);
485 
486         // Step 2 and 3, the sequence doesn't matter.
487         JsReplyProxy mainFrameReplyProxy = null;
488         JsReplyProxy iframeReplyProxy = null;
489         Rect rect = null;
490         for (int i = 0; i < 2; ++i) {
491             TestWebMessageListener.Data data = webMessageListener.waitForOnPostMessage();
492             if (data.mIsMainFrame) {
493                 // The connection between browser and main frame established.
494                 Assert.assertEquals("init", data.mMessage);
495                 mainFrameReplyProxy = data.mReplyProxy;
496             } else {
497                 // The connection between browser and iframe established.
498                 iframeReplyProxy = data.mReplyProxy;
499                 // iframe_link location.
500                 String[] c = data.mMessage.split(";");
501                 rect = new Rect(Integer.parseInt(c[0]), Integer.parseInt(c[1]),
502                         Integer.parseInt(c[2]), Integer.parseInt(c[3]));
503             }
504         }
505 
506         Assert.assertNotNull("rect should not be null", rect);
507         Assert.assertNotNull("mainFrameReplyProxy should not be null.", mainFrameReplyProxy);
508         Assert.assertNotNull("iframeReplyProxy should not be null.", iframeReplyProxy);
509 
510         // Step 4. Click iframe_link to give user gesture.
511         DOMUtils.clickRect(mParentContents.getWebContents(), rect);
512 
513         // Step 5. Waits until the element got clicked.
514         TestWebMessageListener.Data clicked = webMessageListener.waitForOnPostMessage();
515         Assert.assertEquals("clicked", clicked.mMessage);
516 
517         // Step 6. Send an arbitrary message to call window.open on javascript: URI.
518         iframeReplyProxy.postMessage("hello");
519         TestWebMessageListener.Data data = webMessageListener.waitForOnPostMessage();
520         Assert.assertEquals("done", data.mMessage);
521 
522         // Step 7. Send an arbitrary message to trigger the check. Main frame will check if there is
523         // an injected element by running |windowOpenJavaScript|.
524         mainFrameReplyProxy.postMessage("hello");
525 
526         // If |succeed| received, then there was no injection.
527         TestWebMessageListener.Data data2 = webMessageListener.waitForOnPostMessage();
528         Assert.assertEquals("succeed", data2.mMessage);
529 
530         // Cleanup the test web server.
531         crossOriginWebServer.shutdown();
532     }
533 
534     // Copied from imeTest.java.
assertWaitForSelectActionBarStatus( boolean show, final SelectionPopupController controller)535     private void assertWaitForSelectActionBarStatus(
536             boolean show, final SelectionPopupController controller) {
537         CriteriaHelper.pollUiThread(() -> {
538             Criteria.checkThat(controller.isSelectActionBarShowing(), Matchers.is(show));
539         });
540     }
541 
hideSelectActionMode(final SelectionPopupController controller)542     private void hideSelectActionMode(final SelectionPopupController controller) {
543         InstrumentationRegistry.getInstrumentation().runOnMainSync(
544                 () -> controller.destroySelectActionMode());
545     }
546 }
547