Home | History | Annotate | Download | only in test
      1 // Copyright 2012 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.app.Activity;
      8 import android.content.pm.ActivityInfo;
      9 import android.test.suitebuilder.annotation.SmallTest;
     10 import android.view.View;
     11 import android.view.ViewConfiguration;
     12 
     13 import org.chromium.android_webview.AwContents;
     14 import org.chromium.android_webview.AwSettings;
     15 import org.chromium.base.ThreadUtils;
     16 import org.chromium.base.test.util.Feature;
     17 
     18 import java.util.Locale;
     19 import java.util.concurrent.Callable;
     20 
     21 /**
     22  * A test suite for zooming-related methods and settings.
     23  */
     24 public class AwZoomTest extends AwTestBase {
     25     private TestAwContentsClient mContentsClient;
     26     private AwContents mAwContents;
     27     private static final float MAXIMUM_SCALE = 2.0f;
     28 
     29     @Override
     30     public void setUp() throws Exception {
     31         super.setUp();
     32         mContentsClient = new TestAwContentsClient();
     33         final AwTestContainerView testContainerView =
     34                 createAwTestContainerViewOnMainSync(mContentsClient);
     35         mAwContents = testContainerView.getAwContents();
     36     }
     37 
     38     private String getZoomableHtml(float scale) {
     39         final int divWidthPercent = (int) (100.0f / scale);
     40         return String.format(Locale.US, "<html><head><meta name=\"viewport\" content=\"" +
     41                 "width=device-width, minimum-scale=%f, maximum-scale=%f, initial-scale=%f" +
     42                 "\"/></head><body style='margin:0'>" +
     43                 "<div style='width:%d%%;height:100px;border:1px solid black'>Zoomable</div>" +
     44                 "</body></html>",
     45                 scale, MAXIMUM_SCALE, scale, divWidthPercent);
     46     }
     47 
     48     private String getNonZoomableHtml() {
     49         // This page can't be zoomed because its viewport fully occupies
     50         // view area and is explicitly made non user-scalable.
     51         return "<html><head>" +
     52                 "<meta name=\"viewport\" " +
     53                 "content=\"width=device-width,height=device-height," +
     54                 "initial-scale=1,maximum-scale=1,user-scalable=no\">" +
     55                 "</head><body>Non-zoomable</body></html>";
     56     }
     57 
     58     private boolean isMultiTouchZoomSupportedOnUiThread() throws Throwable {
     59         return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
     60             @Override
     61             public Boolean call() throws Exception {
     62                 return mAwContents.isMultiTouchZoomSupported();
     63             }
     64         });
     65     }
     66 
     67     private int getVisibilityOnUiThread(final View view) throws Throwable {
     68         return runTestOnUiThreadAndGetResult(new Callable<Integer>() {
     69             @Override
     70             public Integer call() throws Exception {
     71                 return view.getVisibility();
     72             }
     73         });
     74     }
     75 
     76     private View getZoomControlsOnUiThread() throws Throwable {
     77         return runTestOnUiThreadAndGetResult(new Callable<View>() {
     78             @Override
     79             public View call() throws Exception {
     80                 return mAwContents.getZoomControlsForTest();
     81             }
     82         });
     83     }
     84 
     85     private void invokeZoomPickerOnUiThread() throws Throwable {
     86         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
     87             @Override
     88             public void run() {
     89                 mAwContents.invokeZoomPicker();
     90             }
     91         });
     92     }
     93 
     94     private void zoomInOnUiThreadAndWait() throws Throwable {
     95         final float previousScale = getPixelScaleOnUiThread(mAwContents);
     96         assertTrue(runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
     97             @Override
     98             public Boolean call() throws Exception {
     99                 return mAwContents.zoomIn();
    100             }
    101         }));
    102         // The zoom level is updated asynchronously.
    103         waitForScaleChange(previousScale);
    104     }
    105 
    106     private void zoomOutOnUiThreadAndWait() throws Throwable {
    107         final float previousScale = getPixelScaleOnUiThread(mAwContents);
    108         assertTrue(runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
    109             @Override
    110             public Boolean call() throws Exception {
    111                 return mAwContents.zoomOut();
    112             }
    113         }));
    114         // The zoom level is updated asynchronously.
    115         waitForScaleChange(previousScale);
    116     }
    117 
    118     private void zoomByOnUiThreadAndWait(final float delta) throws Throwable {
    119         final float previousScale = getPixelScaleOnUiThread(mAwContents);
    120         assertTrue(runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
    121             @Override
    122             public Boolean call() throws Exception {
    123                 return mAwContents.zoomBy(delta);
    124             }
    125         }));
    126         // The zoom level is updated asynchronously.
    127         waitForScaleChange(previousScale);
    128     }
    129 
    130     private void waitForScaleChange(final float previousScale) throws Throwable {
    131         poll(new Callable<Boolean>() {
    132             @Override
    133             public Boolean call() throws Exception {
    134                 return previousScale != getPixelScaleOnUiThread(mAwContents);
    135             }
    136         });
    137     }
    138 
    139     private void waitForScaleToBecome(final float expectedScale) throws Throwable {
    140         poll(new Callable<Boolean>() {
    141             @Override
    142             public Boolean call() throws Exception {
    143                 return expectedScale == getScaleOnUiThread(mAwContents);
    144             }
    145         });
    146     }
    147 
    148     private void waitUntilCanNotZoom() throws Throwable {
    149         poll(new Callable<Boolean>() {
    150             @Override
    151             public Boolean call() throws Exception {
    152                 return !canZoomInOnUiThread(mAwContents) &&
    153                         !canZoomOutOnUiThread(mAwContents);
    154             }
    155         });
    156     }
    157 
    158     private void runMagnificationTest() throws Throwable {
    159         getAwSettingsOnUiThread(mAwContents).setUseWideViewPort(true);
    160         assertFalse("Should not be able to zoom in", canZoomInOnUiThread(mAwContents));
    161         final float pageMinimumScale = 0.5f;
    162         loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
    163                 getZoomableHtml(pageMinimumScale), "text/html", false);
    164         waitForScaleToBecome(pageMinimumScale);
    165         assertTrue("Should be able to zoom in", canZoomInOnUiThread(mAwContents));
    166         assertFalse("Should not be able to zoom out", canZoomOutOnUiThread(mAwContents));
    167 
    168         while (canZoomInOnUiThread(mAwContents)) {
    169             zoomInOnUiThreadAndWait();
    170         }
    171         assertTrue("Should be able to zoom out", canZoomOutOnUiThread(mAwContents));
    172 
    173         while (canZoomOutOnUiThread(mAwContents)) {
    174             zoomOutOnUiThreadAndWait();
    175         }
    176         assertTrue("Should be able to zoom in", canZoomInOnUiThread(mAwContents));
    177 
    178         zoomByOnUiThreadAndWait(4.0f);
    179         waitForScaleToBecome(MAXIMUM_SCALE);
    180 
    181         zoomByOnUiThreadAndWait(0.5f);
    182         waitForScaleToBecome(MAXIMUM_SCALE * 0.5f);
    183 
    184         zoomByOnUiThreadAndWait(0.01f);
    185         waitForScaleToBecome(pageMinimumScale);
    186     }
    187 
    188     @SmallTest
    189     @Feature({"AndroidWebView"})
    190     public void testMagnification() throws Throwable {
    191         getAwSettingsOnUiThread(mAwContents).setSupportZoom(true);
    192         runMagnificationTest();
    193     }
    194 
    195     // According to Android CTS test, zoomIn/Out must work
    196     // even if supportZoom is turned off.
    197     @SmallTest
    198     @Feature({"AndroidWebView"})
    199     public void testMagnificationWithZoomSupportOff() throws Throwable {
    200         getAwSettingsOnUiThread(mAwContents).setSupportZoom(false);
    201         runMagnificationTest();
    202     }
    203 
    204     @SmallTest
    205     @Feature({"AndroidWebView"})
    206     public void testZoomUsingMultiTouch() throws Throwable {
    207         AwSettings webSettings = getAwSettingsOnUiThread(mAwContents);
    208         loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
    209                 getZoomableHtml(0.5f), "text/html", false);
    210 
    211         assertTrue(webSettings.supportZoom());
    212         assertFalse(webSettings.getBuiltInZoomControls());
    213         assertFalse(isMultiTouchZoomSupportedOnUiThread());
    214 
    215         webSettings.setBuiltInZoomControls(true);
    216         assertTrue(isMultiTouchZoomSupportedOnUiThread());
    217 
    218         webSettings.setSupportZoom(false);
    219         assertFalse(isMultiTouchZoomSupportedOnUiThread());
    220     }
    221 
    222     @SmallTest
    223     @Feature({"AndroidWebView"})
    224     public void testZoomControls() throws Throwable {
    225         AwSettings webSettings = getAwSettingsOnUiThread(mAwContents);
    226         webSettings.setUseWideViewPort(true);
    227         assertFalse("Should not be able to zoom in", canZoomInOnUiThread(mAwContents));
    228         final float pageMinimumScale = 0.5f;
    229         loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
    230                 getZoomableHtml(pageMinimumScale), "text/html", false);
    231         waitForScaleToBecome(pageMinimumScale);
    232         // It must be possible to zoom in (or zoom out) for zoom controls to be shown
    233         assertTrue("Should be able to zoom in", canZoomInOnUiThread(mAwContents));
    234 
    235         assertTrue(webSettings.supportZoom());
    236         webSettings.setBuiltInZoomControls(true);
    237         webSettings.setDisplayZoomControls(false);
    238 
    239         // With DisplayZoomControls set to false, attempts to display zoom
    240         // controls must be ignored.
    241         assertNull(getZoomControlsOnUiThread());
    242         invokeZoomPickerOnUiThread();
    243         assertNull(getZoomControlsOnUiThread());
    244 
    245         webSettings.setDisplayZoomControls(true);
    246         assertNull(getZoomControlsOnUiThread());
    247         invokeZoomPickerOnUiThread();
    248         View zoomControls = getZoomControlsOnUiThread();
    249         assertEquals(View.VISIBLE, getVisibilityOnUiThread(zoomControls));
    250     }
    251 
    252     @SmallTest
    253     @Feature({"AndroidWebView"})
    254     public void testZoomControlsOnNonZoomableContent() throws Throwable {
    255         AwSettings webSettings = getAwSettingsOnUiThread(mAwContents);
    256         loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
    257                 getNonZoomableHtml(), "text/html", false);
    258 
    259         // ContentView must update itself according to the viewport setup.
    260         waitUntilCanNotZoom();
    261 
    262         assertTrue(webSettings.supportZoom());
    263         webSettings.setBuiltInZoomControls(true);
    264         webSettings.setDisplayZoomControls(true);
    265         assertNull(getZoomControlsOnUiThread());
    266         invokeZoomPickerOnUiThread();
    267         View zoomControls = getZoomControlsOnUiThread();
    268         assertEquals(View.GONE, getVisibilityOnUiThread(zoomControls));
    269     }
    270 
    271     @SmallTest
    272     @Feature({"AndroidWebView"})
    273     public void testZoomControlsOnOrientationChange() throws Throwable {
    274         AwSettings webSettings = getAwSettingsOnUiThread(mAwContents);
    275         loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
    276                 getZoomableHtml(0.5f), "text/html", false);
    277 
    278         assertTrue(webSettings.supportZoom());
    279         webSettings.setBuiltInZoomControls(true);
    280         webSettings.setDisplayZoomControls(true);
    281         invokeZoomPickerOnUiThread();
    282 
    283         // Now force an orientation change, and try to display the zoom picker
    284         // again. Make sure that we don't crash when the ZoomPicker registers
    285         // it's receiver.
    286 
    287         Activity activity = getActivity();
    288         int orientation = activity.getRequestedOrientation();
    289         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    290         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    291         activity.setRequestedOrientation(orientation);
    292         invokeZoomPickerOnUiThread();
    293 
    294         // We may crash shortly (as the zoom picker has a short delay in it before
    295         // it tries to register it's BroadcastReceiver), so sleep to verify we don't.
    296         // The delay is encoded in ZoomButtonsController#ZOOM_CONTROLS_TIMEOUT,
    297         // if that changes we may need to update this test.
    298         Thread.sleep(ViewConfiguration.getZoomControlsTimeout());
    299     }
    300 }
    301