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.
5 package org
.chromium
.chrome
.browser
.infobar
;
7 import android
.graphics
.Rect
;
8 import android
.graphics
.Region
;
9 import android
.test
.FlakyTest
;
10 import android
.test
.suitebuilder
.annotation
.MediumTest
;
11 import android
.view
.ViewGroup
;
12 import android
.view
.ViewTreeObserver
;
14 import org
.chromium
.base
.ThreadUtils
;
15 import org
.chromium
.base
.test
.util
.Feature
;
16 import org
.chromium
.chrome
.browser
.ChromeActivity
;
17 import org
.chromium
.chrome
.browser
.Tab
;
18 import org
.chromium
.chrome
.browser
.preferences
.NetworkPredictionOptions
;
19 import org
.chromium
.chrome
.browser
.preferences
.PrefServiceBridge
;
20 import org
.chromium
.chrome
.test
.ChromeActivityTestCaseBase
;
21 import org
.chromium
.chrome
.test
.util
.InfoBarTestAnimationListener
;
22 import org
.chromium
.chrome
.test
.util
.InfoBarUtil
;
23 import org
.chromium
.chrome
.test
.util
.TestHttpServerClient
;
24 import org
.chromium
.content
.browser
.test
.util
.Criteria
;
25 import org
.chromium
.content
.browser
.test
.util
.CriteriaHelper
;
27 import java
.util
.ArrayList
;
28 import java
.util
.concurrent
.Callable
;
29 import java
.util
.concurrent
.ExecutionException
;
30 import java
.util
.concurrent
.TimeoutException
;
31 import java
.util
.concurrent
.atomic
.AtomicInteger
;
36 * TODO(newt): merge this with InfoBarTest after upstreaming.
38 public class InfoBarTest2
extends ChromeActivityTestCaseBase
<ChromeActivity
> {
39 static class MutableBoolean
{
40 public boolean mValue
= false;
43 private InfoBarTestAnimationListener mListener
;
45 public InfoBarTest2() {
46 super(ChromeActivity
.class);
50 protected void setUp() throws Exception
{
53 // Register for animation notifications
54 InfoBarContainer container
=
55 getActivity().getActivityTab().getInfoBarContainer();
56 mListener
= new InfoBarTestAnimationListener();
57 container
.setAnimationListener(mListener
);
60 // Adds an infobar to the currrent tab. Blocks until the infobar has been added.
61 protected void addInfoBarToCurrentTab(final InfoBar infoBar
) throws InterruptedException
{
62 getInstrumentation().runOnMainSync(new Runnable() {
65 Tab tab
= getActivity().getActivityTab();
66 assertTrue("Failed to find tab.", tab
!= null);
67 tab
.getInfoBarContainer().addInfoBar(infoBar
);
70 assertTrue("InfoBar not added.", mListener
.addInfoBarAnimationFinished());
71 getInstrumentation().waitForIdleSync();
74 // Removes an infobar from the currrent tab. Blocks until the infobar has been removed.
75 protected void removeInfoBarFromCurrentTab(final InfoBar infoBar
) throws InterruptedException
{
76 getInstrumentation().runOnMainSync(new Runnable() {
79 Tab tab
= getActivity().getActivityTab();
80 assertTrue("Failed to find tab.", tab
!= null);
81 tab
.getInfoBarContainer().removeInfoBar(infoBar
);
84 assertTrue("InfoBar not removed.", mListener
.removeInfoBarAnimationFinished());
85 getInstrumentation().waitForIdleSync();
88 // Dismisses the passed infobar. Blocks until the bar has been removed.
89 protected void dismissInfoBar(final InfoBar infoBar
) throws InterruptedException
{
90 getInstrumentation().runOnMainSync(new Runnable() {
93 infoBar
.dismissJavaOnlyInfoBar();
96 assertTrue("InfoBar not removed.", mListener
.removeInfoBarAnimationFinished());
97 getInstrumentation().waitForIdleSync();
100 protected ArrayList
<Integer
> getInfoBarIdsForCurrentTab() {
101 final ArrayList
<Integer
> infoBarIds
= new ArrayList
<Integer
>();
102 getInstrumentation().runOnMainSync(new Runnable() {
105 Tab tab
= getActivity().getActivityTab();
106 assertTrue("Failed to find tab.", tab
!= null);
107 for (InfoBar infoBar
: tab
.getInfoBarContainer().getInfoBars()) {
108 infoBarIds
.add(infoBar
.getId());
116 * Verifies that infobars added from Java expire or not as expected.
119 @Feature({"Browser"})
120 public void testInfoBarExpiration() throws InterruptedException
{
121 // First add an infobar that expires.
122 final MutableBoolean dismissed
= new MutableBoolean();
123 MessageInfoBar expiringInfoBar
= new MessageInfoBar("Hello!");
124 expiringInfoBar
.setDismissedListener(new InfoBarListeners
.Dismiss() {
126 public void onInfoBarDismissed(InfoBar infoBar
) {
127 dismissed
.mValue
= true;
130 expiringInfoBar
.setExpireOnNavigation(true);
131 addInfoBarToCurrentTab(expiringInfoBar
);
133 // Verify it's really there.
134 ArrayList
<Integer
> infoBarIds
= getInfoBarIdsForCurrentTab();
135 assertEquals(1, infoBarIds
.size());
136 assertEquals(expiringInfoBar
.getId(), infoBarIds
.get(0).intValue());
138 // Now navigate, it should expire.
139 loadUrl(TestHttpServerClient
.getUrl("chrome/test/data/android/google.html"));
140 assertTrue("InfoBar not removed.", mListener
.removeInfoBarAnimationFinished());
141 assertTrue("InfoBar did not expire on navigation.", dismissed
.mValue
);
142 infoBarIds
= getInfoBarIdsForCurrentTab();
143 assertTrue(infoBarIds
.isEmpty());
145 // Now test a non-expiring infobar.
146 MessageInfoBar persistentInfoBar
= new MessageInfoBar("Hello!");
147 persistentInfoBar
.setDismissedListener(new InfoBarListeners
.Dismiss() {
149 public void onInfoBarDismissed(InfoBar infoBar
) {
150 dismissed
.mValue
= true;
153 dismissed
.mValue
= false;
154 persistentInfoBar
.setExpireOnNavigation(false);
155 addInfoBarToCurrentTab(persistentInfoBar
);
157 // Navigate, it should still be there.
158 loadUrl(TestHttpServerClient
.getUrl("chrome/test/data/android/google.html"));
159 assertFalse("InfoBar did expire on navigation.", dismissed
.mValue
);
160 infoBarIds
= getInfoBarIdsForCurrentTab();
161 assertEquals(1, infoBarIds
.size());
162 assertEquals(persistentInfoBar
.getId(), infoBarIds
.get(0).intValue());
164 // Close the infobar.
165 dismissInfoBar(persistentInfoBar
);
168 // Define function to pass parameter to Runnable to be used in testInfoBarExpirationNoPrerender.
169 private Runnable
setNetworkPredictionOptions(
170 final NetworkPredictionOptions networkPredictionOptions
) {
171 return new Runnable() {
174 PrefServiceBridge
.getInstance().setNetworkPredictionOptions(
175 networkPredictionOptions
);
181 * Same as testInfoBarExpiration but with prerender turned-off.
182 * The behavior when prerender is on/off is different as in the prerender case the infobars are
183 * added when we swap tabs.
184 * @throws InterruptedException
187 @Feature({"Browser"})
188 public void testInfoBarExpirationNoPrerender() throws InterruptedException
, ExecutionException
{
189 // Save prediction preference.
190 NetworkPredictionOptions networkPredictionOption
=
191 ThreadUtils
.runOnUiThreadBlocking(new Callable
<NetworkPredictionOptions
>() {
193 public NetworkPredictionOptions
call() {
194 return PrefServiceBridge
.getInstance().getNetworkPredictionOptions();
198 ThreadUtils
.runOnUiThreadBlocking(setNetworkPredictionOptions(
199 NetworkPredictionOptions
.NETWORK_PREDICTION_NEVER
));
200 testInfoBarExpiration();
202 // Make sure we restore prediction preference.
203 ThreadUtils
.runOnUiThreadBlocking(setNetworkPredictionOptions(networkPredictionOption
));
208 * Tests that adding and then immediately removing an infobar works as expected (and does not
212 @Feature({"Browser"})
213 public void testQuickAddOneAndRemove() throws InterruptedException
{
214 final InfoBar infoBar
= new MessageInfoBar("Hello");
215 addInfoBarToCurrentTab(infoBar
);
216 removeInfoBarFromCurrentTab(infoBar
);
217 assertTrue(getInfoBarIdsForCurrentTab().isEmpty());
221 * Tests that adding and then immediately dismissing an infobar works as expected (and does not
225 @Feature({"Browser"})
226 public void testQuickAddOneAndDismiss() throws InterruptedException
{
227 final InfoBar infoBar
= new MessageInfoBar("Hello");
228 addInfoBarToCurrentTab(infoBar
);
229 dismissInfoBar(infoBar
);
230 assertTrue(getInfoBarIdsForCurrentTab().isEmpty());
234 * Tests that adding 2 infobars and then immediately removing the last one works as expected.
235 * This scenario is special as the 2nd infobar does not even get added to the view hierarchy.
238 @Feature({"Browser"})
239 public void testAddTwoAndRemoveOneQuick() throws InterruptedException
{
240 final InfoBar infoBar1
= new MessageInfoBar("One");
241 final InfoBar infoBar2
= new MessageInfoBar("Two");
242 getInstrumentation().runOnMainSync(new Runnable() {
245 Tab tab
= getActivity().getActivityTab();
246 assertTrue("Failed to find tab.", tab
!= null);
247 tab
.getInfoBarContainer().addInfoBar(infoBar1
);
248 tab
.getInfoBarContainer().addInfoBar(infoBar2
);
249 tab
.getInfoBarContainer().removeInfoBar(infoBar2
);
253 // We should get an infobar added event for the first infobar.
254 assertTrue("InfoBar not added.", mListener
.addInfoBarAnimationFinished());
256 // But no infobar removed event as the 2nd infobar was removed before it got added.
257 assertFalse("InfoBar not removed.", mListener
.removeInfoBarAnimationFinished());
258 ArrayList
<Integer
> infoBarIds
= getInfoBarIdsForCurrentTab();
259 assertEquals(1, infoBarIds
.size());
260 assertEquals(infoBar1
.getId(), infoBarIds
.get(0).intValue());
264 * Tests that adding 2 infobars and then immediately dismissing the last one works as expected
265 * This scenario is special as the 2nd infobar does not even get added to the view hierarchy.
268 @Feature({"Browser"})
269 public void testAddTwoAndDismissOneQuick() throws InterruptedException
{
270 final InfoBar infoBar1
= new MessageInfoBar("One");
271 final InfoBar infoBar2
= new MessageInfoBar("Two");
272 getInstrumentation().runOnMainSync(new Runnable() {
275 Tab tab
= getActivity().getActivityTab();
276 assertTrue("Failed to find tab.", tab
!= null);
277 tab
.getInfoBarContainer().addInfoBar(infoBar1
);
278 tab
.getInfoBarContainer().addInfoBar(infoBar2
);
279 infoBar2
.dismissJavaOnlyInfoBar();
283 // We should get an infobar added event for the first infobar.
284 assertTrue("InfoBar not added.", mListener
.addInfoBarAnimationFinished());
286 // But no infobar removed event as the 2nd infobar was removed before it got added.
287 assertFalse("InfoBar not removed.", mListener
.removeInfoBarAnimationFinished());
288 ArrayList
<Integer
> infoBarIds
= getInfoBarIdsForCurrentTab();
289 assertEquals(1, infoBarIds
.size());
290 assertEquals(infoBar1
.getId(), infoBarIds
.get(0).intValue());
294 * Tests that we don't assert when a tab is getting closed while an infobar is being shown and
298 @Feature({"Browser"})
299 public void testCloseTabOnAdd() throws InterruptedException
{
300 loadUrl(TestHttpServerClient
.getUrl(
301 "chrome/test/data/android/google.html"));
303 final InfoBar infoBar
= new MessageInfoBar("Hello");
304 addInfoBarToCurrentTab(infoBar
);
305 getInstrumentation().runOnMainSync(new Runnable() {
308 Tab tab
= getActivity().getActivityTab();
309 assertTrue("Failed to find tab.", tab
!= null);
310 tab
.getInfoBarContainer().removeInfoBar(infoBar
);
311 getActivity().getTabModelSelector().closeTab(tab
);
317 * Tests that the x button in the infobar does close the infobar and that the event is not
318 * propagated to the ContentView.
320 * @Feature({"Browser"})
321 * Bug: http://crbug.com/172427
324 public void testCloseButton() throws InterruptedException
, TimeoutException
{
325 loadUrl(TestHttpServerClient
.getUrl(
326 "chrome/test/data/android/click_listener.html"));
327 final InfoBar infoBar
= new MessageInfoBar("Hello");
328 getInstrumentation().runOnMainSync(new Runnable() {
331 Tab tab
= getActivity().getActivityTab();
332 assertTrue("Failed to find tab.", tab
!= null);
333 tab
.getInfoBarContainer().addInfoBar(infoBar
);
336 assertTrue("InfoBar not added", mListener
.addInfoBarAnimationFinished());
338 // Now press the close button.
339 assertTrue("Close button wasn't found", InfoBarUtil
.clickCloseButton(infoBar
));
340 assertTrue("Infobar not removed.", mListener
.removeInfoBarAnimationFinished());
342 // The page should not have received the click.
343 assertTrue("The page recieved the click.",
344 !Boolean
.parseBoolean(runJavaScriptCodeInCurrentTab("wasClicked")));
348 * Tests that adding and removing correctly manages the transparent region, which allows for
349 * optimizations in SurfaceFlinger (less overlays).
352 @Feature({"Browser"})
353 public void testAddAndDismissSurfaceFlingerOverlays() throws InterruptedException
{
354 final ViewGroup decorView
= (ViewGroup
) getActivity().getWindow().getDecorView();
355 final InfoBarContainer infoBarContainer
=
356 getActivity().getActivityTab().getInfoBarContainer();
358 // Detect layouts. Note this doesn't actually need to be atomic (just final).
359 final AtomicInteger layoutCount
= new AtomicInteger();
360 getInstrumentation().runOnMainSync(new Runnable() {
363 decorView
.getViewTreeObserver().addOnGlobalLayoutListener(
364 new ViewTreeObserver
.OnGlobalLayoutListener() {
366 public void onGlobalLayout() {
367 layoutCount
.incrementAndGet();
373 // First add an infobar.
374 final InfoBar infoBar
= new MessageInfoBar("Hello");
375 addInfoBarToCurrentTab(infoBar
);
377 // A layout must occur to recalculate the transparent region.
378 boolean layoutOccured
= CriteriaHelper
.pollForUIThreadCriteria(
381 public boolean isSatisfied() {
382 return layoutCount
.get() > 0;
385 assertTrue(layoutOccured
);
387 final Rect fullDisplayFrame
= new Rect();
388 final Rect fullDisplayFrameMinusContainer
= new Rect();
389 final Rect containerDisplayFrame
= new Rect();
391 getInstrumentation().runOnMainSync(new Runnable() {
394 decorView
.getWindowVisibleDisplayFrame(fullDisplayFrame
);
395 decorView
.getWindowVisibleDisplayFrame(fullDisplayFrameMinusContainer
);
396 fullDisplayFrameMinusContainer
.bottom
-= infoBarContainer
.getHeight();
397 int windowLocation
[] = new int[2];
398 infoBarContainer
.getLocationInWindow(windowLocation
);
399 containerDisplayFrame
.set(
402 windowLocation
[0] + infoBarContainer
.getWidth(),
403 windowLocation
[1] + infoBarContainer
.getHeight());
405 // The InfoBarContainer subtracts itself from the transparent region.
406 Region transparentRegion
= new Region(fullDisplayFrame
);
407 infoBarContainer
.gatherTransparentRegion(transparentRegion
);
408 assertEquals(transparentRegion
.getBounds(), fullDisplayFrameMinusContainer
);
412 // Now remove the infobar.
414 dismissInfoBar(infoBar
);
416 // A layout must occur to recalculate the transparent region.
417 layoutOccured
= CriteriaHelper
.pollForUIThreadCriteria(
420 public boolean isSatisfied() {
421 return layoutCount
.get() > 0;
424 assertTrue(layoutOccured
);
426 getInstrumentation().runOnMainSync(new Runnable() {
429 // The InfoBarContainer should no longer be subtracted from the transparent region.
430 // We really want assertTrue(transparentRegion.contains(containerDisplayFrame)),
431 // but region doesn't have 'contains(Rect)', so we invert the test. So, the old
432 // container rect can't touch the bounding rect of the non-transparent region).
433 Region transparentRegion
= new Region();
434 decorView
.gatherTransparentRegion(transparentRegion
);
435 Region opaqueRegion
= new Region(fullDisplayFrame
);
436 opaqueRegion
.op(transparentRegion
, Region
.Op
.DIFFERENCE
);
437 assertFalse(opaqueRegion
.getBounds().intersect(containerDisplayFrame
));
441 // Additional manual test that this is working:
442 // - adb shell dumpsys SurfaceFlinger
443 // - Observe that Clank's overlay size changes (or disappears if URLbar is also gone).
447 public void startMainActivity() throws InterruptedException
{
448 startMainActivityOnBlankPage();