1 // Copyright 2013 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.test.suitebuilder.annotation.MediumTest; 8 9 import org.chromium.android_webview.AwContents; 10 import org.chromium.android_webview.AwContentsClient; 11 import org.chromium.android_webview.AwSettings; 12 import org.chromium.base.test.util.Feature; 13 import org.chromium.content.browser.test.util.CallbackHelper; 14 import org.chromium.ui.gfx.DeviceDisplayInfo; 15 16 import java.util.Locale; 17 import java.util.concurrent.Callable; 18 19 /** 20 * Tests for legacy quirks (compatibility with WebView Classic). 21 */ 22 public class AwLegacyQuirksTest extends AwTestBase { 23 24 @MediumTest 25 @Feature({"AndroidWebView"}) 26 public void testTargetDensityDpi() throws Throwable { 27 final TestAwContentsClient contentClient = new TestAwContentsClient(); 28 final AwTestContainerView testContainerView = 29 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 30 final AwContents awContents = testContainerView.getAwContents(); 31 AwSettings settings = getAwSettingsOnUiThread(awContents); 32 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 33 34 final String pageTemplate = "<html><head>" + 35 "<meta name='viewport' content='width=device-width, target-densityDpi=%s' />" + 36 "</head><body onload='document.title=document.body.clientWidth'></body></html>"; 37 final String pageDeviceDpi = String.format((Locale) null, pageTemplate, "device-dpi"); 38 final String pageHighDpi = String.format((Locale) null, pageTemplate, "high-dpi"); 39 final String pageDpi100 = String.format((Locale) null, pageTemplate, "100"); 40 41 settings.setJavaScriptEnabled(true); 42 43 DeviceDisplayInfo deviceInfo = 44 DeviceDisplayInfo.create(getInstrumentation().getTargetContext()); 45 loadDataSync(awContents, onPageFinishedHelper, pageDeviceDpi, "text/html", false); 46 int actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 47 assertEquals(deviceInfo.getDisplayWidth(), actualWidth, 10f); 48 49 float displayWidth = (deviceInfo.getDisplayWidth()); 50 float deviceDpi = (float) (160f * deviceInfo.getDIPScale()); 51 52 loadDataSync(awContents, onPageFinishedHelper, pageHighDpi, "text/html", false); 53 actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 54 assertEquals(displayWidth * (240f / deviceDpi), actualWidth, 10f); 55 56 loadDataSync(awContents, onPageFinishedHelper, pageDpi100, "text/html", false); 57 actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 58 assertEquals(displayWidth * (100f / deviceDpi), actualWidth, 10f); 59 } 60 61 @MediumTest 62 @Feature({"AndroidWebView"}) 63 public void testWideViewportInitialScaleDoesNotExpandFixedLayoutWidth() throws Throwable { 64 final TestAwContentsClient contentClient = new TestAwContentsClient(); 65 final AwTestContainerView testContainerView = 66 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 67 final AwContents awContents = testContainerView.getAwContents(); 68 AwSettings settings = getAwSettingsOnUiThread(awContents); 69 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 70 71 final String page = "<html><head>" + 72 "<meta name='viewport' content='width=device-width, initial-scale=0.5' />" + 73 "</head><body onload='document.title=document.body.clientWidth'></body></html>"; 74 75 settings.setJavaScriptEnabled(true); 76 settings.setUseWideViewPort(true); 77 78 DeviceDisplayInfo deviceInfo = 79 DeviceDisplayInfo.create(getInstrumentation().getTargetContext()); 80 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 81 float displayWidth = (float) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale()); 82 int actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 83 assertEquals(displayWidth, actualWidth, 10f); 84 assertEquals(1.0f, getScaleOnUiThread(awContents)); 85 } 86 87 @MediumTest 88 @Feature({"AndroidWebView"}) 89 public void testZeroValuesQuirk() throws Throwable { 90 final TestAwContentsClient contentClient = new TestAwContentsClient(); 91 final AwTestContainerView testContainerView = 92 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 93 final AwContents awContents = testContainerView.getAwContents(); 94 AwSettings settings = getAwSettingsOnUiThread(awContents); 95 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 96 97 final String page = "<html><head>" + 98 "<meta name='viewport' content='width=0, height=0, initial-scale=0.0, " + 99 " minimum-scale=0.0, maximum-scale=0.0' />" + 100 "</head><body onload='document.title=document.body.clientWidth'></body></html>"; 101 102 settings.setJavaScriptEnabled(true); 103 104 DeviceDisplayInfo deviceInfo = 105 DeviceDisplayInfo.create(getInstrumentation().getTargetContext()); 106 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 107 float displayWidth = (float) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale()); 108 int actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 109 assertEquals(displayWidth, actualWidth, 10f); 110 assertEquals(1.0f, getScaleOnUiThread(awContents)); 111 112 settings.setUseWideViewPort(true); 113 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 114 actualWidth = Integer.parseInt(getTitleOnUiThread(awContents)); 115 assertEquals(displayWidth, actualWidth, 10f); 116 assertEquals(1.0f, getScaleOnUiThread(awContents)); 117 } 118 119 @MediumTest 120 @Feature({"AndroidWebView"}) 121 public void testScreenSizeInPhysicalPixelsQuirk() throws Throwable { 122 final TestAwContentsClient contentClient = new TestAwContentsClient(); 123 final AwTestContainerView testContainerView = 124 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 125 final AwContents awContents = testContainerView.getAwContents(); 126 AwSettings settings = getAwSettingsOnUiThread(awContents); 127 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 128 129 settings.setJavaScriptEnabled(true); 130 131 loadUrlSync(awContents, onPageFinishedHelper, "about:blank"); 132 133 DeviceDisplayInfo deviceInfo = 134 DeviceDisplayInfo.create(getInstrumentation().getTargetContext()); 135 float dipScale = (float) deviceInfo.getDIPScale(); 136 float physicalDisplayWidth = deviceInfo.getPhysicalDisplayWidth() != 0 ? 137 deviceInfo.getPhysicalDisplayWidth() : 138 deviceInfo.getDisplayWidth(); 139 float cssDisplayWidth = physicalDisplayWidth / dipScale; 140 float physicalDisplayHeight = deviceInfo.getPhysicalDisplayHeight() != 0 ? 141 deviceInfo.getPhysicalDisplayHeight() : 142 deviceInfo.getDisplayHeight(); 143 float cssDisplayHeight = physicalDisplayHeight / dipScale; 144 145 float screenWidth = Integer.parseInt( 146 executeJavaScriptAndWaitForResult(awContents, contentClient, "screen.width")); 147 assertEquals(physicalDisplayWidth, screenWidth, 10f); 148 float screenAvailWidth = Integer.parseInt( 149 executeJavaScriptAndWaitForResult(awContents, contentClient, "screen.availWidth")); 150 assertEquals(physicalDisplayWidth, screenAvailWidth, 10f); 151 float outerWidth = Integer.parseInt( 152 executeJavaScriptAndWaitForResult(awContents, contentClient, "outerWidth")); 153 float innerWidth = Integer.parseInt( 154 executeJavaScriptAndWaitForResult(awContents, contentClient, "innerWidth")); 155 assertEquals(innerWidth * dipScale, outerWidth, 10f); 156 String deviceWidthEqualsScreenWidth = executeJavaScriptAndWaitForResult(awContents, 157 contentClient, 158 "matchMedia(\"screen and (device-width:" + (int) screenWidth + "px)\").matches"); 159 assertEquals("true", deviceWidthEqualsScreenWidth); 160 161 float screenHeight = Integer.parseInt( 162 executeJavaScriptAndWaitForResult(awContents, contentClient, "screen.height")); 163 assertEquals(physicalDisplayHeight, screenHeight, 10f); 164 float screenAvailHeight = Integer.parseInt( 165 executeJavaScriptAndWaitForResult(awContents, contentClient, "screen.availHeight")); 166 assertEquals(physicalDisplayHeight, screenAvailHeight, 10f); 167 float outerHeight = Integer.parseInt( 168 executeJavaScriptAndWaitForResult(awContents, contentClient, "outerHeight")); 169 float innerHeight = Integer.parseInt( 170 executeJavaScriptAndWaitForResult(awContents, contentClient, "innerHeight")); 171 assertEquals(innerHeight * dipScale, outerHeight, 10f); 172 String deviceHeightEqualsScreenHeight = executeJavaScriptAndWaitForResult(awContents, 173 contentClient, 174 "matchMedia(\"screen and (device-height:" + (int) screenHeight + "px)\").matches"); 175 assertEquals("true", deviceHeightEqualsScreenHeight); 176 } 177 178 @MediumTest 179 @Feature({"AndroidWebView"}) 180 public void testMetaMergeContentQuirk() throws Throwable { 181 final TestAwContentsClient contentClient = new TestAwContentsClient(); 182 final AwTestContainerView testContainerView = 183 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 184 final AwContents awContents = testContainerView.getAwContents(); 185 AwSettings settings = getAwSettingsOnUiThread(awContents); 186 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 187 188 final int pageWidth = 3000; 189 final float pageScale = 1.0f; 190 final String page = String.format((Locale) null, "<html><head>" + 191 "<meta name='viewport' content='width=%d' />" + 192 "<meta name='viewport' content='initial-scale=%.1f' />" + 193 "<meta name='viewport' content='user-scalable=0' />" + 194 "</head><body onload='document.title=document.body.clientWidth'></body></html>", 195 pageWidth, pageScale); 196 197 settings.setJavaScriptEnabled(true); 198 settings.setUseWideViewPort(true); 199 settings.setBuiltInZoomControls(true); 200 settings.setSupportZoom(true); 201 202 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 203 // ContentView must update itself according to the viewport setup. 204 // As we specify 'user-scalable=0', the page must become non-zoomable. 205 poll(new Callable<Boolean>() { 206 @Override 207 public Boolean call() throws Exception { 208 return !canZoomInOnUiThread(awContents) && !canZoomOutOnUiThread(awContents); 209 } 210 }); 211 int width = Integer.parseInt(getTitleOnUiThread(awContents)); 212 assertEquals(pageWidth, width); 213 assertEquals(pageScale, getScaleOnUiThread(awContents)); 214 } 215 216 @MediumTest 217 @Feature({"AndroidWebView"}) 218 public void testMetaMergeContentQuirkOverrides() throws Throwable { 219 final TestAwContentsClient contentClient = new TestAwContentsClient(); 220 final AwTestContainerView testContainerView = 221 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 222 final AwContents awContents = testContainerView.getAwContents(); 223 AwSettings settings = getAwSettingsOnUiThread(awContents); 224 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 225 226 final int pageWidth = 3000; 227 final String page = String.format((Locale) null, "<html><head>" + 228 "<meta name='viewport' content='width=device-width' />" + 229 "<meta name='viewport' content='width=%d' />" + 230 "</head><body onload='document.title=document.body.clientWidth'></body></html>", 231 pageWidth); 232 233 settings.setJavaScriptEnabled(true); 234 settings.setUseWideViewPort(true); 235 236 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 237 int width = Integer.parseInt(getTitleOnUiThread(awContents)); 238 assertEquals(pageWidth, width); 239 } 240 241 @MediumTest 242 @Feature({"AndroidWebView"}) 243 public void testInitialScaleClobberQuirk() throws Throwable { 244 final TestAwContentsClient contentClient = new TestAwContentsClient(); 245 final AwTestContainerView testContainerView = 246 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 247 final AwContents awContents = testContainerView.getAwContents(); 248 AwSettings settings = getAwSettingsOnUiThread(awContents); 249 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 250 251 final String pageTemplate = "<html><head>" + 252 "<meta name='viewport' content='initial-scale=%d' />" + 253 "</head><body>" + 254 "<div style='width:10000px;height:200px'>A big div</div>" + 255 "</body></html>"; 256 final String pageScale4 = String.format((Locale) null, pageTemplate, 4); 257 final String page = String.format((Locale) null, pageTemplate, 1); 258 259 // Page scale updates are asynchronous. There is an issue that we can't 260 // reliably check, whether the scale as NOT changed (i.e. remains to be 1.0). 261 // So we first change the scale to some non-default value, and then wait 262 // until it gets back to 1.0. 263 int onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount(); 264 loadDataSync(awContents, onPageFinishedHelper, pageScale4, "text/html", false); 265 contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount); 266 assertEquals(4.0f, getScaleOnUiThread(awContents)); 267 // The following call to set initial scale will be ignored. However, a temporary 268 // page scale change may occur, and this makes the usual onScaleChanged-based workflow 269 // flaky. So instead, we are just polling the scale until it becomes 1.0. 270 settings.setInitialPageScale(50); 271 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 272 ensureScaleBecomes(1.0f, awContents); 273 } 274 275 @MediumTest 276 @Feature({"AndroidWebView"}) 277 public void testNoUserScalableQuirk() throws Throwable { 278 final TestAwContentsClient contentClient = new TestAwContentsClient(); 279 final AwTestContainerView testContainerView = 280 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 281 final AwContents awContents = testContainerView.getAwContents(); 282 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 283 284 final String pageScale4 = "<html><head>" + 285 "<meta name='viewport' content='initial-scale=4' />" + 286 "</head><body>" + 287 "<div style='width:10000px;height:200px'>A big div</div>" + 288 "</body></html>"; 289 final String page = "<html><head>" + 290 "<meta name='viewport' " + 291 "content='width=device-width,initial-scale=2,user-scalable=no' />" + 292 "</head><body>" + 293 "<div style='width:10000px;height:200px'>A big div</div>" + 294 "</body></html>"; 295 296 // Page scale updates are asynchronous. There is an issue that we can't 297 // reliably check, whether the scale as NOT changed (i.e. remains to be 1.0). 298 // So we first change the scale to some non-default value, and then wait 299 // until it gets back to 1.0. 300 int onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount(); 301 loadDataSync(awContents, onPageFinishedHelper, pageScale4, "text/html", false); 302 contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount); 303 assertEquals(4.0f, getScaleOnUiThread(awContents)); 304 onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount(); 305 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 306 contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount); 307 assertEquals(1.0f, getScaleOnUiThread(awContents)); 308 } 309 310 // background shorthand property must not override background-size when 311 // it's already set. 312 @MediumTest 313 @Feature({"AndroidWebView", "Preferences"}) 314 public void testUseLegacyBackgroundSizeShorthandBehavior() throws Throwable { 315 final TestAwContentsClient contentClient = new TestAwContentsClient(); 316 final AwTestContainerView testContainerView = 317 createAwTestContainerViewOnMainSyncInQuirksMode(contentClient); 318 final AwContents awContents = testContainerView.getAwContents(); 319 AwSettings settings = getAwSettingsOnUiThread(awContents); 320 CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper(); 321 final String expectedBackgroundSize = "cover"; 322 final String page = "<html><head>" + 323 "<script>" + 324 "function getBackgroundSize() {" + 325 " var e = document.getElementById('test'); " + 326 " e.style.backgroundSize = '" + expectedBackgroundSize + "';" + 327 " e.style.background = 'center red url(dummy://test.png) no-repeat border-box'; " + 328 " return e.style.backgroundSize; " + 329 "}" + 330 "</script></head>" + 331 "<body onload='document.title=getBackgroundSize()'>" + 332 " <div id='test'> </div>" + 333 "</body></html>"; 334 settings.setJavaScriptEnabled(true); 335 loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false); 336 String actualBackgroundSize = getTitleOnUiThread(awContents); 337 assertEquals(expectedBackgroundSize, actualBackgroundSize); 338 } 339 340 private AwTestContainerView createAwTestContainerViewOnMainSyncInQuirksMode( 341 final AwContentsClient client) throws Exception { 342 return createAwTestContainerViewOnMainSync(client, true); 343 } 344 345 private void ensureScaleBecomes(final float targetScale, final AwContents awContents) 346 throws Throwable { 347 poll(new Callable<Boolean>() { 348 @Override 349 public Boolean call() throws Exception { 350 return targetScale == getScaleOnUiThread(awContents); 351 } 352 }); 353 } 354 } 355