Index: chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java |
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..87386cfd2fad31aaac19ce07860d4803bafbcac4 |
--- /dev/null |
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java |
@@ -0,0 +1,505 @@ |
+// 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.chrome.browser.fullscreen; |
+ |
+import android.graphics.Rect; |
+import android.graphics.Region; |
+import android.os.Build; |
+import android.os.SystemClock; |
+import android.test.FlakyTest; |
+import android.test.suitebuilder.annotation.LargeTest; |
+import android.test.suitebuilder.annotation.MediumTest; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.view.ViewTreeObserver; |
+import android.view.WindowManager; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.base.test.util.Feature; |
+import org.chromium.base.test.util.UrlUtils; |
+import org.chromium.chrome.browser.ChromeWebContentsDelegateAndroid; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.omnibox.UrlBar; |
+import org.chromium.chrome.browser.tab.ChromeTab; |
+import org.chromium.chrome.test.ChromeTabbedActivityTestBase; |
+import org.chromium.chrome.test.util.OmniboxTestUtils; |
+import org.chromium.chrome.test.util.PrerenderTestHelper; |
+import org.chromium.chrome.test.util.TestHttpServerClient; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content.browser.test.util.Criteria; |
+import org.chromium.content.browser.test.util.CriteriaHelper; |
+import org.chromium.content.browser.test.util.TestTouchUtils; |
+import org.chromium.content.browser.test.util.UiUtils; |
+ |
+import java.util.concurrent.Callable; |
+import java.util.concurrent.ExecutionException; |
+import java.util.concurrent.atomic.AtomicInteger; |
+ |
+/** |
+ * Test suite for verifying the behavior of various fullscreen actions. |
+ */ |
+public class FullscreenManagerTest extends ChromeTabbedActivityTestBase { |
+ |
+ private static final String LONG_HTML_TEST_PAGE = UrlUtils.encodeHtmlDataUri( |
+ "<html><body style='height:10000px;'></body></html>"); |
+ private static final String LONG_FULLSCREEN_API_HTML_TEST_PAGE = UrlUtils.encodeHtmlDataUri( |
+ "<html>" |
+ + "<head>" |
+ + " <meta name=\"viewport\" " |
+ + " content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" />" |
+ + " <script>" |
+ + " function toggleFullScreen() {" |
+ + " if (document.webkitIsFullScreen) {" |
+ + " document.webkitCancelFullScreen();" |
+ + " } else {" |
+ + " document.body.webkitRequestFullScreen();" |
+ + " }" |
+ + " };" |
+ + " </script>" |
+ + " <style>" |
+ + " body:-webkit-full-screen { background: red; width: 100%; }" |
+ + " </style>" |
+ + "</head>" |
+ + "<body style='height:10000px;' onclick='toggleFullScreen();'>" |
+ + "</body>" |
+ + "</html>"); |
+ |
+ @MediumTest |
+ @Feature({"Fullscreen"}) |
+ public void testTogglePersistentFullscreen() throws InterruptedException { |
+ startMainActivityWithURL(LONG_HTML_TEST_PAGE); |
+ |
+ Tab tab = getActivity().getActivityTab(); |
+ final ChromeWebContentsDelegateAndroid delegate = |
+ tab.getChromeWebContentsDelegateAndroid(); |
+ |
+ assertTrue(waitForFullscreenFlag(tab, false)); |
+ assertTrue(waitForPersistentFullscreen(delegate, false)); |
+ |
+ togglePersistentFullscreen(delegate, true); |
+ assertTrue(waitForFullscreenFlag(tab, true)); |
+ assertTrue(waitForPersistentFullscreen(delegate, true)); |
+ |
+ togglePersistentFullscreen(delegate, false); |
+ assertTrue(waitForFullscreenFlag(tab, false)); |
+ assertTrue(waitForPersistentFullscreen(delegate, false)); |
+ } |
+ |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ public void testPersistentFullscreenChangingUiFlags() throws InterruptedException { |
+ // Exiting fullscreen via UI Flags is not supported in versions prior to MR2. |
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return; |
+ |
+ startMainActivityWithURL(LONG_HTML_TEST_PAGE); |
+ |
+ final Tab tab = getActivity().getActivityTab(); |
+ final ChromeWebContentsDelegateAndroid delegate = |
+ tab.getChromeWebContentsDelegateAndroid(); |
+ |
+ assertTrue(waitForFullscreenFlag(tab, false)); |
+ assertTrue(waitForPersistentFullscreen(delegate, false)); |
+ |
+ togglePersistentFullscreen(delegate, true); |
+ assertTrue(waitForFullscreenFlag(tab, true)); |
+ assertTrue(waitForPersistentFullscreen(delegate, true)); |
+ |
+ // There is a race condition in android when setting various system UI flags. |
+ // Adding this wait to allow the animation transitions to complete before continuing |
+ // the test (See https://b/10387660) |
+ UiUtils.settleDownUI(getInstrumentation()); |
+ |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ View view = tab.getContentViewCore().getContainerView(); |
+ view.setSystemUiVisibility( |
+ view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_FULLSCREEN); |
+ } |
+ }); |
+ assertTrue(waitForFullscreenFlag(tab, true)); |
+ assertTrue(waitForPersistentFullscreen(delegate, true)); |
+ } |
+ |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ public void testExitPersistentFullscreenAllowsManualFullscreen() |
+ throws InterruptedException, ExecutionException { |
+ startMainActivityWithURL(LONG_FULLSCREEN_API_HTML_TEST_PAGE); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.setAnimationDurationsForTest(1, 1); |
+ int topControlsHeight = fullscreenManager.getTopControlsHeight(); |
+ |
+ Tab tab = getActivity().getActivityTab(); |
+ View view = tab.getView(); |
+ final ChromeWebContentsDelegateAndroid delegate = |
+ tab.getChromeWebContentsDelegateAndroid(); |
+ |
+ singleClickView(view, view.getWidth() / 2, view.getHeight() / 2); |
+ waitForPersistentFullscreen(delegate, true); |
+ assertEquals((float) -topControlsHeight, waitForTopControlsPosition(-topControlsHeight)); |
+ |
+ TestTouchUtils.sleepForDoubleTapTimeout(getInstrumentation()); |
+ singleClickView(view, view.getWidth() / 2, view.getHeight() / 2); |
+ waitForPersistentFullscreen(delegate, false); |
+ waitForNoBrowserTopControlsOffset(); |
+ assertEquals((float) 0, waitForTopControlsPosition(0)); |
+ |
+ scrollTopControls(false); |
+ scrollTopControls(true); |
+ } |
+ |
+ /** |
+ * Marked flaky on 2015-05-15: http://crbug.com/488393 |
+ * @LargeTest |
+ * @Feature({"Fullscreen"}) |
+ */ |
+ @FlakyTest |
+ public void testManualHidingShowingTopControls() |
+ throws InterruptedException, ExecutionException { |
+ startMainActivityWithURL(LONG_HTML_TEST_PAGE); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.disableBrowserOverrideForTest(); |
+ |
+ assertEquals(fullscreenManager.getControlOffset(), 0f); |
+ |
+ scrollTopControls(false); |
+ // Reverse the scroll and ensure the controls come back into view. |
+ scrollTopControls(true); |
+ // Check that the URL bar has not grabbed focus (http://crbug/236365) |
+ UrlBar urlBar = (UrlBar) getActivity().findViewById(R.id.url_bar); |
+ assertFalse("Url bar grabbed focus", urlBar.hasFocus()); |
+ } |
+ |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ public void testHidingTopControlsRemovesSurfaceFlingerOverlay() |
+ throws InterruptedException, ExecutionException { |
+ startMainActivityWithURL(LONG_HTML_TEST_PAGE); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.disableBrowserOverrideForTest(); |
+ |
+ assertEquals(fullscreenManager.getControlOffset(), 0f); |
+ |
+ // Detect layouts. Note this doesn't actually need to be atomic (just final). |
+ final AtomicInteger layoutCount = new AtomicInteger(); |
+ getActivity().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener( |
+ new ViewTreeObserver.OnGlobalLayoutListener() { |
+ @Override |
+ public void onGlobalLayout() { |
+ layoutCount.incrementAndGet(); |
+ } |
+ }); |
+ |
+ // When the top-controls are removed, we need a layout to trigger the |
+ // transparent region for the app to be updated. |
+ scrollTopControls(false); |
+ boolean layoutOccured = CriteriaHelper.pollForUIThreadCriteria( |
+ new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ return layoutCount.get() > 0; |
+ } |
+ }); |
+ assertTrue(layoutOccured); |
+ |
+ getInstrumentation().runOnMainSync(new Runnable() { |
+ @Override |
+ public void run() { |
+ // Check that when the top controls are gone, the entire decorView is contained |
+ // in the transparent region of the app. |
+ Rect visibleDisplayFrame = new Rect(); |
+ Region transparentRegion = new Region(); |
+ ViewGroup decorView = (ViewGroup) getActivity().getWindow().getDecorView(); |
+ decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame); |
+ decorView.gatherTransparentRegion(transparentRegion); |
+ assertTrue(transparentRegion.quickContains(visibleDisplayFrame)); |
+ } |
+ }); |
+ |
+ // Additional manual test that this is working: |
+ // - adb shell dumpsys SurfaceFlinger |
+ // - Observe that there is no 'Chrome' related overlay listed, only 'Surfaceview'. |
+ } |
+ |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ public void testManualFullscreenDisabledForChromePages() |
+ throws InterruptedException, ExecutionException { |
+ // The credits page was chosen as it is a chrome:// page that is long and would support |
+ // manual fullscreen if it were supported. |
+ startMainActivityWithURL("chrome://credits"); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.disableBrowserOverrideForTest(); |
+ int topControlsHeight = fullscreenManager.getTopControlsHeight(); |
+ |
+ assertEquals(fullscreenManager.getControlOffset(), 0f); |
+ |
+ float dragX = 50f; |
+ float dragStartY = topControlsHeight * 2; |
+ float dragFullY = dragStartY - topControlsHeight; |
+ |
+ long downTime = SystemClock.uptimeMillis(); |
+ dragStart(dragX, dragStartY, downTime); |
+ dragTo(dragX, dragX, dragStartY, dragFullY, 100, downTime); |
+ assertEquals(0f, waitForTopControlsPosition(0f)); |
+ dragEnd(dragX, dragFullY, downTime); |
+ assertEquals(0f, waitForTopControlsPosition(0f)); |
+ } |
+ |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ public void testControlsShownOnUnresponsiveRenderer() |
+ throws InterruptedException, ExecutionException { |
+ startMainActivityWithURL(LONG_HTML_TEST_PAGE); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.setAnimationDurationsForTest(1, 1); |
+ assertTrue(waitForNoBrowserTopControlsOffset()); |
+ assertEquals(fullscreenManager.getControlOffset(), 0f); |
+ |
+ scrollTopControls(false); |
+ |
+ Tab tab = getActivity().getActivityTab(); |
+ final ChromeWebContentsDelegateAndroid delegate = |
+ tab.getChromeWebContentsDelegateAndroid(); |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ delegate.rendererUnresponsive(); |
+ } |
+ }); |
+ assertEquals(0f, waitForTopControlsPosition(0f)); |
+ |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ delegate.rendererResponsive(); |
+ } |
+ }); |
+ assertTrue(waitForNoBrowserTopControlsOffset()); |
+ } |
+ |
+ /* |
+ @LargeTest |
+ @Feature({"Fullscreen"}) |
+ @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) |
+ crbug.com/339668 |
+ */ |
+ @FlakyTest |
+ public void testPrerenderedPageSupportsManualHiding() |
+ throws InterruptedException, ExecutionException { |
+ startMainActivityOnBlankPage(); |
+ |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ fullscreenManager.disableBrowserOverrideForTest(); |
+ |
+ final Tab tab = getActivity().getActivityTab(); |
+ final String testUrl = TestHttpServerClient.getUrl( |
+ "chrome/test/data/android/very_long_google.html"); |
+ PrerenderTestHelper.trainAutocompleteActionPredictorAndTestPrerender(testUrl, this); |
+ assertTrue("loadUrl did not use pre-rendered page.", |
+ PrerenderTestHelper.isLoadUrlResultPrerendered(loadUrl(testUrl))); |
+ |
+ UrlBar urlBar = (UrlBar) getActivity().findViewById(R.id.url_bar); |
+ OmniboxTestUtils.toggleUrlBarFocus(urlBar, false); |
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
+ @Override |
+ public void run() { |
+ ChromeTab.fromTab(tab).processEnableFullscreenRunnableForTest(); |
+ } |
+ }); |
+ |
+ scrollTopControls(false); |
+ } |
+ |
+ private void scrollTopControls(boolean show) throws InterruptedException, ExecutionException { |
+ ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ int topControlsHeight = fullscreenManager.getTopControlsHeight(); |
+ |
+ waitForPageToBeScrollable(getActivity().getActivityTab()); |
+ |
+ float dragX = 50f; |
+ // Use a larger scroll range than the height of the top controls to ensure we overcome |
+ // the delay in a scroll start being sent. |
+ float dragStartY = topControlsHeight * 3; |
+ float dragEndY = dragStartY - topControlsHeight * 2; |
+ float expectedPosition = -topControlsHeight; |
+ if (show) { |
+ expectedPosition = 0f; |
+ float tempDragStartY = dragStartY; |
+ dragStartY = dragEndY; |
+ dragEndY = tempDragStartY; |
+ } |
+ long downTime = SystemClock.uptimeMillis(); |
+ dragStart(dragX, dragStartY, downTime); |
+ dragTo(dragX, dragX, dragStartY, dragEndY, 100, downTime); |
+ assertEquals(expectedPosition, waitForTopControlsPosition(expectedPosition)); |
+ dragEnd(dragX, dragEndY, downTime); |
+ assertEquals(expectedPosition, waitForTopControlsPosition(expectedPosition)); |
+ } |
+ |
+ private void togglePersistentFullscreen(final ChromeWebContentsDelegateAndroid delegate, |
+ final boolean state) { |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ delegate.toggleFullscreenModeForTab(state); |
+ } |
+ }); |
+ } |
+ |
+ private static boolean isFlagSet(int flags, int flag) { |
+ return (flags & flag) == flag; |
+ } |
+ |
+ private boolean isFullscreenFlagSet(final Tab tab, final boolean state) { |
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { |
+ View view = tab.getContentViewCore().getContainerView(); |
+ int visibility = view.getSystemUiVisibility(); |
+ // SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN should only be used during the transition between |
+ // fullscreen states, so it should always be cleared when fullscreen transitions are |
+ // completed. |
+ return (!isFlagSet(visibility, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) |
+ && (isFlagSet(visibility, View.SYSTEM_UI_FLAG_FULLSCREEN) == state); |
+ } else { |
+ WindowManager.LayoutParams attributes = |
+ getActivity().getWindow().getAttributes(); |
+ return isFlagSet( |
+ attributes.flags, WindowManager.LayoutParams.FLAG_FULLSCREEN) == state; |
+ } |
+ } |
+ |
+ private boolean waitForFullscreenFlag(final Tab tab, final boolean state) |
+ throws InterruptedException { |
+ return CriteriaHelper.pollForCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThread(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() throws Exception { |
+ return isFullscreenFlagSet(tab, state); |
+ } |
+ }).get(); |
+ } catch (Exception e) { |
+ fail(e.getMessage()); |
+ return false; |
+ } |
+ } |
+ }); |
+ } |
+ |
+ private boolean waitForPersistentFullscreen(final ChromeWebContentsDelegateAndroid delegate, |
+ final boolean state) throws InterruptedException { |
+ return CriteriaHelper.pollForCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThread(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() throws Exception { |
+ return state == delegate.isFullscreenForTabOrPending(); |
+ } |
+ }).get(); |
+ } catch (Exception e) { |
+ fail(e.getMessage()); |
+ return false; |
+ } |
+ } |
+ }); |
+ } |
+ |
+ private float waitForTopControlsPosition(final float position) |
+ throws InterruptedException, ExecutionException { |
+ final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ CriteriaHelper.pollForCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThread(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() throws Exception { |
+ return position == fullscreenManager.getControlOffset(); |
+ } |
+ }).get(); |
+ } catch (Exception e) { |
+ fail(e.getMessage()); |
+ return false; |
+ } |
+ } |
+ }); |
+ return ThreadUtils.runOnUiThread(new Callable<Float>() { |
+ @Override |
+ public Float call() throws Exception { |
+ return fullscreenManager.getControlOffset(); |
+ } |
+ }).get(); |
+ } |
+ |
+ private boolean waitForNoBrowserTopControlsOffset() throws InterruptedException { |
+ final ChromeFullscreenManager fullscreenManager = getActivity().getFullscreenManager(); |
+ return CriteriaHelper.pollForCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThread(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() throws Exception { |
+ return !fullscreenManager.hasBrowserControlOffsetOverride(); |
+ } |
+ }).get(); |
+ } catch (Exception e) { |
+ fail(e.getMessage()); |
+ return false; |
+ } |
+ } |
+ }); |
+ } |
+ |
+ private boolean waitForPageToBeScrollable(final Tab tab) throws InterruptedException { |
+ return CriteriaHelper.pollForCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThread(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() throws Exception { |
+ ContentViewCore contentViewCore = tab.getContentViewCore(); |
+ return contentViewCore.computeVerticalScrollRange() |
+ > contentViewCore.getContainerView().getHeight(); |
+ } |
+ }).get(); |
+ } catch (Exception e) { |
+ fail(e.getMessage()); |
+ return false; |
+ } |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ protected void startMainActivityWithURL(String url) throws InterruptedException { |
+ super.startMainActivityWithURL(url); |
+ final Tab tab = getActivity().getActivityTab(); |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ ChromeTab.fromTab(tab).processEnableFullscreenRunnableForTest(); |
+ } |
+ }); |
+ } |
+ |
+ @Override |
+ public void startMainActivity() throws InterruptedException { |
+ // Each test will start itself with the appropriate test page. |
+ } |
+} |