aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Volodine <timvolodine@google.com>2019-02-08 18:30:16 +0000
committertimvolodine <timvolodine@google.com>2019-02-14 16:10:18 +0000
commitaa6c899803ee44a943e6401128f985955125fd44 (patch)
treec7c1a85cd733316721c157b18e7cc854bc22e022
parent230d4d1a9f34e8894df6af2d27238f772df1ae23 (diff)
downloadBrowser2-aa6c899803ee44a943e6401128f985955125fd44.tar.gz
[Browser2] Sync from chromium code, including increase target_sdk version to 28 (P) and enable cleartext traffic
Update Browser2 to match the SystemWebViewShell code in the chromium tree. Update Browser2 to target Android P (API level 28). This is a part of the general effort to update apps to API level 28, see crbug.com/898190 and b/115499280. This patch also ensures that cleartextTrafficPermitted is set to true to ensure backwards compatibility for the Browser2 app (and to allow cleartext traffic, e.g. using unencrypted http protocol). This patch also updates trust-anchors to include system and user certificates. Bug: 119501137 Test: manual Change-Id: I17a3989a5a39e57f3dff4d63a8722bd846d0fe47
-rw-r--r--AndroidManifest.xml61
-rw-r--r--res/drawable-mdpi/breadcrumb_arrow_black.pngbin0 -> 295 bytes
-rw-r--r--res/drawable-mdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--res/drawable-mdpi/item_more_black.pngbin0 -> 325 bytes
-rw-r--r--res/layout/activity_empty.xml12
-rw-r--r--res/layout/activity_webview_animation_test.xml65
-rw-r--r--res/layout/activity_webview_browser.xml14
-rw-r--r--res/menu/main_menu.xml9
-rw-r--r--res/values/strings.xml15
-rw-r--r--res/xml/network_security_config.xml17
-rw-r--r--src/org/chromium/webview_shell/JankActivity.java1
-rw-r--r--src/org/chromium/webview_shell/TelemetryActivity.java51
-rw-r--r--src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java9
-rw-r--r--src/org/chromium/webview_shell/WebViewAnimationTestActivity.java157
-rw-r--r--src/org/chromium/webview_shell/WebViewBrowserActivity.java295
15 files changed, 639 insertions, 67 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 95b33ed..4376adc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -10,7 +10,7 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24" />
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<!-- "Normal" permissions which do not require user prompt -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -31,35 +31,47 @@
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
- android:theme="@android:style/Theme.Light" >
+ android:theme="@android:style/Theme.Light"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:debuggable="true" >
+ <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ android:value="true" />
<activity
- android:name=".TelemetryActivity"
+ android:name="org.chromium.webview_shell.TelemetryActivity"
+ android:launchMode="singleTask"
android:label="@string/title_activity_telemetry"
android:exported="true">
</activity>
<activity
- android:name=".TelemetryMemoryPressureActivity"
+ android:name="org.chromium.webview_shell.TelemetryMemoryPressureActivity"
android:launchMode="singleTask"
android:label="@string/title_activity_telemetry"
android:exported="true">
</activity>
<activity
- android:name=".JankActivity"
+ android:name="org.chromium.webview_shell.JankActivity"
android:label="@string/title_activity_jank"
android:noHistory="true"
android:exported="true">
</activity>
<activity
- android:name=".StartupTimeActivity"
+ android:name="org.chromium.webview_shell.StartupTimeActivity"
android:label="@string/title_activity_startup_time"
android:noHistory="true"
android:exported="true">
</activity>
<activity
- android:name=".WebViewBrowserActivity"
+ android:name="org.chromium.webview_shell.WebViewCreateDestroyActivity"
+ android:launchMode="singleTask"
+ android:label="@string/title_activity_create_destroy"
+ android:exported="true">
+ </activity>
+ <activity
+ android:name="org.chromium.webview_shell.WebViewBrowserActivity"
android:label="@string/title_activity_browser"
android:exported="true"
- android:windowSoftInputMode="adjustResize">
+ android:windowSoftInputMode="adjustResize"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -85,5 +97,38 @@
<data android:mimeType="application/vnd.wap.xhtml+xml"/> <!-- XHTML MP -->
</intent-filter>
</activity>
+ <activity
+ android:name="org.chromium.webview_shell.WebViewLayoutTestActivity"
+ android:label="@string/title_activity_layout_test"
+ android:exported="true">
+ </activity>
+ <activity
+ android:name="org.chromium.webview_shell.WebViewThreadTestActivity"
+ android:label="@string/title_activity_thread_test"
+ android:exported="true">
+ </activity>
+ <activity android:name="org.chromium.test.broker.OnDeviceInstrumentationBroker"
+ android:exported="true"/>
+
+ <activity
+ android:name="org.chromium.webview_shell.PageCyclerTestActivity"
+ android:label="@string/title_activity_page_cycler"
+ android:exported="true">
+ </activity>
+
+ <activity
+ android:name="org.chromium.webview_shell.WebViewTracingActivity"
+ android:label="@string/title_activity_telemetry"
+ android:noHistory="true"
+ android:exported="true">
+ </activity>
+
+ <activity
+ android:name="org.chromium.webview_shell.WebViewAnimationTestActivity"
+ android:noHistory="true"
+ android:exported="true">
+ </activity>
+
+ <uses-library android:name="android.test.runner" />
</application>
</manifest>
diff --git a/res/drawable-mdpi/breadcrumb_arrow_black.png b/res/drawable-mdpi/breadcrumb_arrow_black.png
new file mode 100644
index 0000000..7b9ff79
--- /dev/null
+++ b/res/drawable-mdpi/breadcrumb_arrow_black.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/res/drawable-mdpi/item_more_black.png b/res/drawable-mdpi/item_more_black.png
new file mode 100644
index 0000000..b984062
--- /dev/null
+++ b/res/drawable-mdpi/item_more_black.png
Binary files differ
diff --git a/res/layout/activity_empty.xml b/res/layout/activity_empty.xml
new file mode 100644
index 0000000..868453d
--- /dev/null
+++ b/res/layout/activity_empty.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/emptyview">
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/activity_webview_animation_test.xml b/res/layout/activity_webview_animation_test.xml
new file mode 100644
index 0000000..44cf7c0
--- /dev/null
+++ b/res/layout/activity_webview_animation_test.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/translate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/translate_button" />
+ <Button
+ android:id="@+id/scale"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/scale_button" />
+ <Button
+ android:id="@+id/rotate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rotate_button" />
+ <CheckBox
+ android:id="@+id/use_layer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:text="@string/layer_button" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/alpha_button"
+ android:layout_gravity="center_vertical" />
+ <SeekBar
+ android:id="@+id/view_alpha"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:max="100"
+ android:progress="100"
+ android:layout_marginStart="4dp"
+ android:layout_marginEnd="8dp" />
+ </LinearLayout>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="center" />
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/activity_webview_browser.xml b/res/layout/activity_webview_browser.xml
index 07483c5..80464d7 100644
--- a/res/layout/activity_webview_browser.xml
+++ b/res/layout/activity_webview_browser.xml
@@ -13,7 +13,7 @@
android:gravity="center">
<LinearLayout
android:orientation="horizontal"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/url_field"
@@ -22,16 +22,18 @@
android:layout_weight="1.0"
android:singleLine="true"
android:inputType="textUri"
- android:imeOptions="actionGo" />
+ android:selectAllOnFocus="true"
+ android:imeOptions="actionGo"
+ android:importantForAutofill="no" />
<ImageButton
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:src="@drawable/breadcrumb_arrow_black"
android:contentDescription="@string/load_url"
android:onClick="loadUrlFromUrlBar" />
<ImageButton
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:src="@drawable/item_more_black"
android:contentDescription="@string/menu_about"
android:onClick="showPopup" />
diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml
index a22610d..14c90c2 100644
--- a/res/menu/main_menu.xml
+++ b/res/menu/main_menu.xml
@@ -6,6 +6,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_reset_webview"
android:title="@string/menu_reset_webview"/>
+ <item android:id="@+id/menu_clear_cache"
+ android:title="@string/menu_clear_cache"/>
+ <item android:id="@+id/menu_enable_tracing"
+ android:checkable="true"
+ android:title="@string/menu_enable_tracing"/>
+ <item android:id="@+id/menu_print"
+ android:title="@string/menu_print"/>
+ <item android:id="@+id/start_animation_activity"
+ android:title="@string/menu_start_animation_activity"/>
<item android:id="@+id/menu_about"
android:title="@string/menu_about"/>
</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1108c76..c39328a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10,7 +10,22 @@
<string name="title_activity_jank">WebView Jank Tester</string>
<string name="title_activity_startup_time">WebView Startup Time Tester</string>
<string name="title_activity_browser">WebView Browser Tester</string>
+ <string name="title_activity_layout_test">WebView Layout Test</string>
+ <string name="title_activity_thread_test">WebView Thread Test</string>
+ <string name="title_activity_page_cycler">WebView Page Cycler Test</string>
+ <string name="title_activity_create_destroy">WebView Create Destroy</string>
<string name="menu_reset_webview">Destroy and create new WebView</string>
+ <string name="menu_clear_cache">Clear cache</string>
+ <string name="menu_enable_tracing">Enable tracing</string>
+ <string name="menu_start_animation_activity">Animation test</string>
+ <string name="menu_print">Print</string>
<string name="menu_about">About WebView</string>
<string name="load_url">Load URL</string>
+
+ <!-- activity_webview_animation_test strings -->
+ <string name="alpha_button">View Alpha</string>
+ <string name="layer_button">Layer</string>
+ <string name="rotate_button">Rotate</string>
+ <string name="scale_button">Scale</string>
+ <string name="translate_button">Translate</string>
</resources>
diff --git a/res/xml/network_security_config.xml b/res/xml/network_security_config.xml
new file mode 100644
index 0000000..4669e3b
--- /dev/null
+++ b/res/xml/network_security_config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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. -->
+
+<network-security-config>
+ <!-- Starting with Android P (API level 28), the default value of
+ isCleartextTrafficPermitted() is false. For the SystemWebViewShell
+ test browser we explicitly set cleartextTrafficPermitted here to
+ preserve functionality. (crbug.com/898190) -->
+ <base-config cleartextTrafficPermitted="true">
+ <trust-anchors>
+ <certificates src="user"/>
+ <certificates src="system"/>
+ </trust-anchors>
+ </base-config>
+</network-security-config> \ No newline at end of file
diff --git a/src/org/chromium/webview_shell/JankActivity.java b/src/org/chromium/webview_shell/JankActivity.java
index 13390b7..a508e44 100644
--- a/src/org/chromium/webview_shell/JankActivity.java
+++ b/src/org/chromium/webview_shell/JankActivity.java
@@ -28,6 +28,7 @@ public class JankActivity extends Activity {
CookieManager.setAcceptFileSchemeCookies(true);
webView.setWebViewClient(new WebViewClient() {
+ @SuppressWarnings("deprecation") // because we support api level 19 and up.
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
return false;
diff --git a/src/org/chromium/webview_shell/TelemetryActivity.java b/src/org/chromium/webview_shell/TelemetryActivity.java
index 11fa10b..a29e525 100644
--- a/src/org/chromium/webview_shell/TelemetryActivity.java
+++ b/src/org/chromium/webview_shell/TelemetryActivity.java
@@ -1,12 +1,14 @@
// 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.webview_shell;
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
+import android.os.Trace;
import android.webkit.CookieManager;
+import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -14,25 +16,58 @@ import android.webkit.WebViewClient;
* This activity is designed for Telemetry testing of WebView.
*/
public class TelemetryActivity extends Activity {
+ static final String DEFAULT_START_UP_TRACE_TAG = "WebViewStartupInterval";
+ static final String DEFAULT_LOAD_URL_TRACE_TAG = "WebViewBlankUrlLoadInterval";
+ static final String DEFAULT_START_UP_AND_LOAD_URL_TRACE_TAG =
+ "WebViewStartupAndLoadBlankUrlInterval";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setTitle(
getResources().getString(R.string.title_activity_telemetry));
- setContentView(R.layout.activity_webview);
- WebView webView = (WebView) findViewById(R.id.webview);
+ Intent intent = getIntent();
+ final String startUpTraceTag = intent.getStringExtra("WebViewStartUpTraceTag");
+ final String loadUrlTraceTag = intent.getStringExtra("WebViewLoadUrlTraceTag");
+ final String startUpAndLoadUrlTraceTag =
+ intent.getStringExtra("WebViewStartUpAndLoadUrlTraceTag");
+
+ Trace.beginSection(startUpTraceTag == null ? DEFAULT_START_UP_AND_LOAD_URL_TRACE_TAG
+ : startUpAndLoadUrlTraceTag);
+ Trace.beginSection(startUpTraceTag == null ? DEFAULT_START_UP_TRACE_TAG : startUpTraceTag);
+ WebView webView = new WebView(this);
+ setContentView(webView);
+ Trace.endSection();
+
CookieManager.setAcceptFileSchemeCookies(true);
- webView.getSettings().setJavaScriptEnabled(true);
+ WebSettings settings = webView.getSettings();
+ settings.setJavaScriptEnabled(true);
+ settings.setUseWideViewPort(true);
+ settings.setLoadWithOverviewMode(true);
+ settings.setDomStorageEnabled(true);
+ settings.setMediaPlaybackRequiresUserGesture(false);
+ String userAgentString = intent.getStringExtra("userAgent");
+ if (userAgentString != null) {
+ settings.setUserAgentString(userAgentString);
+ }
webView.setWebViewClient(new WebViewClient() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView webView, String url) {
- return false;
- }
+ @SuppressWarnings("deprecation") // because we support api level 19 and up.
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return false;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ Trace.endSection();
+ Trace.endSection();
+ }
});
+ Trace.beginSection(loadUrlTraceTag == null ? DEFAULT_LOAD_URL_TRACE_TAG : loadUrlTraceTag);
webView.loadUrl("about:blank");
}
}
diff --git a/src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java b/src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java
index d9e6dfc..a6a9de8 100644
--- a/src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java
+++ b/src/org/chromium/webview_shell/TelemetryMemoryPressureActivity.java
@@ -30,10 +30,11 @@ public class TelemetryMemoryPressureActivity extends Activity {
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView webView, String url) {
- return false;
- }
+ @SuppressWarnings("deprecation") // because we support api level 19 and up.
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView webView, String url) {
+ return false;
+ }
});
webview.loadUrl("about:blank");
diff --git a/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java b/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java
new file mode 100644
index 0000000..e67b6e5
--- /dev/null
+++ b/src/org/chromium/webview_shell/WebViewAnimationTestActivity.java
@@ -0,0 +1,157 @@
+// Copyright 2019 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.webview_shell;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.webkit.WebView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+/**
+ * Activity to exercise transform animations on WebView.
+ */
+public class WebViewAnimationTestActivity extends Activity {
+ private static final String HTML = "<html>"
+ + " <head>"
+ + " <style type =\"text/css\">"
+ + " .container {"
+ + " display: grid;"
+ + " grid-template-columns: 100px 100px 100px 100px 100px;"
+ + " grid-template-rows: 100px 100px 100px 100px 100px;"
+ + " }"
+ + " .alt1 {"
+ + " background-color: #aaffaa;"
+ + " }"
+ + " .alt2 {"
+ + " background-color: #ff4545;"
+ + " }"
+ + " </style>"
+ + " </head>"
+ + " <body>"
+ + " <div class=\"container\">"
+ + " <div class=\"alt1\">00</div>"
+ + " <div class=\"alt2\">01</div>"
+ + " <div class=\"alt1\">02</div>"
+ + " <div class=\"alt2\">03</div>"
+ + " <div class=\"alt1\">04</div>"
+ + " <div class=\"alt2\">05</div>"
+ + " <div class=\"alt1\">06</div>"
+ + " <div class=\"alt2\">07</div>"
+ + " <div class=\"alt1\">08</div>"
+ + " <div class=\"alt2\">09</div>"
+ + " <div class=\"alt1\">10</div>"
+ + " <div class=\"alt2\">11</div>"
+ + " <div class=\"alt1\">12</div>"
+ + " <div class=\"alt2\">13</div>"
+ + " <div class=\"alt1\">14</div>"
+ + " <div class=\"alt2\">15</div>"
+ + " <div class=\"alt1\">16</div>"
+ + " <div class=\"alt2\">17</div>"
+ + " <div class=\"alt1\">18</div>"
+ + " <div class=\"alt2\">19</div>"
+ + " <div class=\"alt1\">20</div>"
+ + " <div class=\"alt2\">21</div>"
+ + " <div class=\"alt1\">22</div>"
+ + " <div class=\"alt2\">23</div>"
+ + " <div class=\"alt1\">24</div>"
+ + " </div>"
+ + " </body>"
+ + "</html>";
+
+ private WebView mWebView;
+ private boolean mIsWindowHardwareAccelerated;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_webview_animation_test);
+ mWebView = (WebView) findViewById(R.id.webview);
+
+ mIsWindowHardwareAccelerated =
+ (getWindow().getAttributes().flags
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+ != 0;
+ mWebView.setBackgroundColor(0);
+ mWebView.loadDataWithBaseURL("http://foo.bar", HTML, "text/html", null, "http://foo.bar");
+ OnClickListener onClickListner = (View v) -> {
+ switch (v.getId()) {
+ case R.id.translate:
+ runTranslate();
+ break;
+ case R.id.scale:
+ runScale();
+ break;
+ case R.id.rotate:
+ runRotate();
+ break;
+ }
+ };
+ findViewById(R.id.scale).setOnClickListener(onClickListner);
+ findViewById(R.id.translate).setOnClickListener(onClickListner);
+ findViewById(R.id.rotate).setOnClickListener(onClickListner);
+ ((SeekBar) findViewById(R.id.view_alpha))
+ .setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar view, int progress, boolean fromUser) {
+ switch (view.getId()) {
+ case R.id.view_alpha:
+ mWebView.setAlpha(progress / 100f);
+ break;
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+ CheckBox checkBox = ((CheckBox) findViewById(R.id.use_layer));
+ checkBox.setOnCheckedChangeListener(
+ (CompoundButton arg0, boolean checked) -> { setWebViewLayer(checked); });
+ setWebViewLayer(checkBox.isChecked());
+ }
+
+ private void runTranslate() {
+ if (mWebView.getTranslationX() == 0f) {
+ mWebView.animate().translationX(100f).translationY(100f);
+ } else {
+ mWebView.animate().translationX(0f).translationY(0f);
+ }
+ }
+
+ private void runScale() {
+ if (mWebView.getScaleX() == 1f) {
+ mWebView.animate().scaleX(.5f).scaleY(.5f);
+ } else {
+ mWebView.animate().scaleX(1f).scaleY(1f);
+ }
+ }
+
+ private void runRotate() {
+ if (mWebView.getRotationX() == 0f) {
+ mWebView.animate().rotationX(45f).rotationY(45f).rotation(90f);
+ } else {
+ mWebView.animate().rotationX(0f).rotationY(0f).rotation(0f);
+ }
+ }
+
+ private void setWebViewLayer(boolean isOnLayer) {
+ if (isOnLayer) {
+ mWebView.setLayerType(mIsWindowHardwareAccelerated ? View.LAYER_TYPE_HARDWARE
+ : View.LAYER_TYPE_SOFTWARE,
+ null);
+ } else {
+ mWebView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
+}
diff --git a/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index 1da563e..0a7b637 100644
--- a/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -5,6 +5,8 @@
package org.chromium.webview_shell;
import android.Manifest;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@@ -18,39 +20,47 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.StrictMode;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
import android.provider.Browser;
import android.util.Log;
import android.util.SparseArray;
-
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
-
import android.webkit.GeolocationPermissions;
import android.webkit.PermissionRequest;
+import android.webkit.TracingConfig;
+import android.webkit.TracingController;
import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-
import android.widget.EditText;
+import android.widget.FrameLayout;
import android.widget.PopupMenu;
import android.widget.TextView;
+import android.widget.Toast;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -69,6 +79,12 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
// WebKit permissions with no corresponding Android permission can always be granted
private static final String NO_ANDROID_PERMISSION = "NO_ANDROID_PERMISSION";
+ // TODO(timav): Remove these variables after http://crbug.com/626202 is fixed.
+ // The Bundle key for WebView serialized state
+ private static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE";
+ // Maximal size of this state.
+ private static final int MAX_STATE_LENGTH = 300 * 1024;
+
// Map from WebKit permissions to Android permissions
private static final HashMap<String, String> sPermissions;
static {
@@ -88,15 +104,18 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
private EditText mUrlBar;
private WebView mWebView;
+ private View mFullscreenView;
private String mWebViewVersion;
+ private boolean mEnableTracing;
// Each time we make a request, store it here with an int key. onRequestPermissionsResult will
// look up the request in order to grant the approprate permissions.
private SparseArray<PermissionRequest> mPendingRequests = new SparseArray<PermissionRequest>();
- private int mNextRequestKey = 0;
+ private int mNextRequestKey;
// Work around our wonky API by wrapping a geo permission prompt inside a regular
// PermissionRequest.
+ @SuppressLint("NewApi") // GeoPermissionRequest class requires API level 21.
private static class GeoPermissionRequest extends PermissionRequest {
private String mOrigin;
private GeolocationPermissions.Callback mCallback;
@@ -106,20 +125,24 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
mCallback = callback;
}
+ @Override
public Uri getOrigin() {
return Uri.parse(mOrigin);
}
+ @Override
public String[] getResources() {
return new String[] { WebViewBrowserActivity.RESOURCE_GEO };
}
+ @Override
public void grant(String[] resources) {
assert resources.length == 1;
assert WebViewBrowserActivity.RESOURCE_GEO.equals(resources[0]);
mCallback.invoke(mOrigin, true, false);
}
+ @Override
public void deny() {
mCallback.invoke(mOrigin, false, false);
}
@@ -127,6 +150,7 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
// For simplicity, also treat the read access needed for file:// URLs as a regular
// PermissionRequest.
+ @SuppressLint("NewApi") // FilePermissionRequest class requires API level 21.
private class FilePermissionRequest extends PermissionRequest {
private String mOrigin;
@@ -134,14 +158,17 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
mOrigin = origin;
}
+ @Override
public Uri getOrigin() {
return Uri.parse(mOrigin);
}
+ @Override
public String[] getResources() {
return new String[] { WebViewBrowserActivity.RESOURCE_FILE_URL };
}
+ @Override
public void grant(String[] resources) {
assert resources.length == 1;
assert WebViewBrowserActivity.RESOURCE_FILE_URL.equals(resources[0]);
@@ -149,20 +176,62 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
WebViewBrowserActivity.this.mWebView.loadUrl(mOrigin);
}
+ @Override
public void deny() {
// womp womp
}
}
+ private static class TracingLogger extends FileOutputStream {
+ private long mByteCount;
+ private long mChunkCount;
+ private final Activity mActivity;
+
+ public TracingLogger(String fileName, Activity activity) throws FileNotFoundException {
+ super(fileName);
+ mActivity = activity;
+ }
+
+ @Override
+ public void write(byte[] chunk) throws IOException {
+ mByteCount += chunk.length;
+ mChunkCount++;
+ super.write(chunk);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ showDialog(mByteCount);
+ }
+
+ private void showDialog(long nbBytes) {
+ StringBuilder info = new StringBuilder();
+ info.append("Tracing data written to file\n");
+ info.append("number of bytes: " + nbBytes);
+
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog dialog = new AlertDialog.Builder(mActivity)
+ .setTitle("Tracing API")
+ .setMessage(info)
+ .setNeutralButton(" OK ", null)
+ .create();
+ dialog.show();
+ }
+ });
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- WebView.setWebContentsDebuggingEnabled(true);
- }
+ WebView.setWebContentsDebuggingEnabled(true);
setContentView(R.layout.activity_webview_browser);
mUrlBar = (EditText) findViewById(R.id.url_field);
mUrlBar.setOnKeyListener(new OnKeyListener() {
+ @Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
loadUrlFromUrlBar(view);
@@ -172,13 +241,75 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
}
});
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyLog()
+ .penaltyDeath()
+ .build());
+ // Conspicuously omitted: detectCleartextNetwork() and detectFileUriExposure() to permit
+ // http:// and file:// origins.
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
+ .detectActivityLeaks()
+ .detectLeakedClosableObjects()
+ .detectLeakedRegistrationObjects()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDeath()
+ .build());
+
createAndInitializeWebView();
String url = getUrlFromIntent(getIntent());
- if (url != null) {
- setUrlBarText(url);
- setUrlFail(false);
- loadUrlFromUrlBar(mUrlBar);
+ if (url == null) {
+ mWebView.restoreState(savedInstanceState);
+ url = mWebView.getUrl();
+ if (url != null) {
+ // If we have restored state, and that state includes
+ // a loaded URL, we reload. This allows us to keep the
+ // scroll offset, and also doesn't add an additional
+ // navigation history entry.
+ setUrlBarText(url);
+ // The immediately previous loadUrlFromurlbar must
+ // have got as far as calling loadUrl, so there is no
+ // URI parsing error at this point.
+ setUrlFail(false);
+ hideKeyboard(mUrlBar);
+ mWebView.reload();
+ mWebView.requestFocus();
+ return;
+ }
+ // Make sure to load a blank page to make it immediately inspectable with
+ // chrome://inspect.
+ url = "about:blank";
+ }
+ setUrlBarText(url);
+ setUrlFail(false);
+ loadUrlFromUrlBar(mUrlBar);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ // Deliberately don't catch TransactionTooLargeException here.
+ mWebView.saveState(savedInstanceState);
+
+ // TODO(timav): Remove this hack after http://crbug.com/626202 is fixed.
+ // Drop the saved state of it is too long since Android N and above
+ // can't handle large states without a crash.
+ byte[] webViewState = savedInstanceState.getByteArray(SAVE_RESTORE_STATE_KEY);
+ if (webViewState != null && webViewState.length > MAX_STATE_LENGTH) {
+ savedInstanceState.remove(SAVE_RESTORE_STATE_KEY);
+ String message = String.format(
+ Locale.US, "Can't save state: %dkb is too long", webViewState.length / 1024);
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mWebView.canGoBack()) {
+ mWebView.goBack();
+ } else {
+ super.onBackPressed();
}
}
@@ -210,18 +341,18 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
setUrlBarText(url);
}
+ @SuppressWarnings("deprecation") // because we support api level 19 and up.
@Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- String url = request.getUrl().toString();
+ public boolean shouldOverrideUrlLoading(WebView webView, String url) {
// "about:" and "chrome:" schemes are internal to Chromium;
// don't want these to be dispatched to other apps.
if (url.startsWith("about:") || url.startsWith("chrome:")) {
return false;
}
- boolean allowLaunchingApps = request.hasGesture() || request.isRedirect();
- return startBrowsingIntent(WebViewBrowserActivity.this, url, allowLaunchingApps);
+ return startBrowsingIntent(WebViewBrowserActivity.this, url);
}
+ @SuppressWarnings("deprecation") // because we support api level 19 and up.
@Override
public void onReceivedError(WebView view, int errorCode, String description,
String failingUrl) {
@@ -239,6 +370,13 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ // Pre Lollipop versions (< api level 21) do not have PermissionRequest,
+ // hence grant here immediately.
+ callback.invoke(origin, true, false);
+ return;
+ }
+
onPermissionRequest(new GeoPermissionRequest(origin, callback));
}
@@ -246,6 +384,28 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
public void onPermissionRequest(PermissionRequest request) {
WebViewBrowserActivity.this.requestPermissionsForPage(request);
}
+
+ @Override
+ public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
+ if (mFullscreenView != null) {
+ ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView);
+ }
+ mFullscreenView = view;
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getWindow().addContentView(mFullscreenView,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
+ }
+
+ @Override
+ public void onHideCustomView() {
+ if (mFullscreenView == null) {
+ return;
+ }
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView);
+ mFullscreenView = null;
+ }
});
mWebView = webview;
@@ -256,14 +416,16 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
// WebKit permissions which can be granted because either they have no associated Android
// permission or the associated Android permission has been granted
+ @TargetApi(Build.VERSION_CODES.M)
private boolean canGrant(String webkitPermission) {
String androidPermission = sPermissions.get(webkitPermission);
- if (androidPermission == NO_ANDROID_PERMISSION) {
+ if (androidPermission.equals(NO_ANDROID_PERMISSION)) {
return true;
}
return PackageManager.PERMISSION_GRANTED == checkSelfPermission(androidPermission);
}
+ @SuppressLint("NewApi") // PermissionRequest#deny requires API level 21.
private void requestPermissionsForPage(PermissionRequest request) {
// Deny any unrecognized permissions.
for (String webkitPermission : request.getResources()) {
@@ -309,12 +471,14 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
}
@Override
+ @SuppressLint("NewApi") // PermissionRequest#deny requires API level 21.
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
// Verify that we can now grant all the requested permissions. Note that although grant()
// takes a list of permissions, grant() is actually all-or-nothing. If there are any
// requested permissions not included in the granted permissions, all will be denied.
PermissionRequest request = mPendingRequests.get(requestCode);
+ mPendingRequests.delete(requestCode);
for (String webkitPermission : request.getResources()) {
if (!canGrant(webkitPermission)) {
request.deny();
@@ -322,21 +486,14 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
}
}
request.grant(request.getResources());
- mPendingRequests.delete(requestCode);
}
public void loadUrlFromUrlBar(View view) {
String url = mUrlBar.getText().toString();
- try {
- URI uri = new URI(url);
- url = (uri.getScheme() == null) ? "http://" + uri.toString() : uri.toString();
- } catch (URISyntaxException e) {
- String message = "<html><body>URISyntaxException: " + e.getMessage() + "</body></html>";
- mWebView.loadData(message, "text/html", "UTF-8");
- setUrlFail(true);
- return;
- }
-
+ // Parse with android.net.Uri instead of java.net.URI because Uri does no validation. Rather
+ // than failing in the browser, let WebView handle weird URLs. WebView will escape illegal
+ // characters and display error pages for bad URLs like "blah://example.com".
+ if (Uri.parse(url).getScheme() == null) url = "http://" + url;
setUrlBarText(url);
setUrlFail(false);
loadUrl(url);
@@ -347,10 +504,12 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
PopupMenu popup = new PopupMenu(this, v);
popup.setOnMenuItemClickListener(this);
popup.inflate(R.menu.main_menu);
+ popup.getMenu().findItem(R.id.menu_enable_tracing).setChecked(mEnableTracing);
popup.show();
}
@Override
+ @SuppressLint("NewApi") // TracingController related methods require API level 28.
public boolean onMenuItemClick(MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_reset_webview:
@@ -362,6 +521,42 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
}
createAndInitializeWebView();
return true;
+ case R.id.menu_clear_cache:
+ if (mWebView != null) {
+ mWebView.clearCache(true);
+ }
+ return true;
+ case R.id.menu_enable_tracing:
+ mEnableTracing = !mEnableTracing;
+ item.setChecked(mEnableTracing);
+ TracingController tracingController = TracingController.getInstance();
+ if (mEnableTracing) {
+ tracingController.start(
+ new TracingConfig.Builder()
+ .addCategories(TracingConfig.CATEGORIES_WEB_DEVELOPER)
+ .setTracingMode(TracingConfig.RECORD_CONTINUOUSLY)
+ .build());
+ } else {
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ String outFileName = getFilesDir() + "/webview_tracing.json";
+ try {
+ tracingController.stop(new TracingLogger(outFileName, this),
+ Executors.newSingleThreadExecutor());
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ return true;
+ case R.id.start_animation_activity:
+ startActivity(new Intent(this, WebViewAnimationTestActivity.class));
+ return true;
+ case R.id.menu_print:
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+ String jobName = "WebViewShell document";
+ PrintDocumentAdapter printAdapter = mWebView.createPrintDocumentAdapter(jobName);
+ printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build());
+ return true;
case R.id.menu_about:
about();
hideKeyboard(mUrlBar);
@@ -371,18 +566,33 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
}
}
+ // setGeolocationDatabasePath deprecated in api level 24,
+ // but we still use it because we support api level 19 and up.
+ @SuppressWarnings("deprecation")
private void initializeSettings(WebSettings settings) {
+ File appcache = null;
+ File geolocation = null;
+
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ appcache = getDir("appcache", 0);
+ geolocation = getDir("geolocation", 0);
+ StrictMode.setThreadPolicy(oldPolicy);
+
settings.setJavaScriptEnabled(true);
// configure local storage apis and their database paths.
- settings.setAppCachePath(getDir("appcache", 0).getPath());
- settings.setGeolocationDatabasePath(getDir("geolocation", 0).getPath());
- settings.setDatabasePath(getDir("databases", 0).getPath());
+ settings.setAppCachePath(appcache.getPath());
+ settings.setGeolocationDatabasePath(geolocation.getPath());
settings.setAppCacheEnabled(true);
settings.setGeolocationEnabled(true);
settings.setDatabaseEnabled(true);
settings.setDomStorageEnabled(true);
+
+ // Default layout behavior for chrome on android.
+ settings.setUseWideViewPort(true);
+ settings.setLoadWithOverviewMode(true);
+ settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
}
private void about() {
@@ -404,7 +614,7 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
.setPositiveButton("OK", null)
.create();
dialog.show();
- dialog.getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+ dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
// Returns true is a method has no arguments and returns either a boolean or a String.
@@ -461,8 +671,7 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
+ ")"
+ "(.*)");
- private static boolean startBrowsingIntent(Context context, String url,
- boolean allowLaunchingApps) {
+ private static boolean startBrowsingIntent(Context context, String url) {
Intent intent;
// Perform generic parsing of the URI to turn it into an Intent.
try {
@@ -492,12 +701,16 @@ public class WebViewBrowserActivity extends Activity implements PopupMenu.OnMenu
// same application can be opened in the same tab.
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
- if (allowLaunchingApps) {
- context.startActivity(intent);
- }
+ context.startActivity(intent);
return true;
} catch (ActivityNotFoundException ex) {
Log.w(TAG, "No application can handle " + url);
+ } catch (SecurityException ex) {
+ // This can happen if the Activity is exported="true", guarded by a permission, and sets
+ // up an intent filter matching this intent. This is a valid configuration for an
+ // Activity, so instead of crashing, we catch the exception and do nothing. See
+ // https://crbug.com/808494 and https://crbug.com/889300.
+ Log.w(TAG, "SecurityException when starting intent for " + url);
}
return false;
}