aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Volodine <timvolodine@google.com>2019-02-15 12:42:13 -0800
committerandroid-build-merger <android-build-merger@google.com>2019-02-15 12:42:13 -0800
commit22b6f0db9912ab9fbb0168e1526f690411c71059 (patch)
treec7c1a85cd733316721c157b18e7cc854bc22e022
parentf80442a6a3358918f0ce0d9d619f707ebdc0d84b (diff)
parent1bcf52c56633007056862292df247f9a8b20dcd5 (diff)
downloadBrowser2-22b6f0db9912ab9fbb0168e1526f690411c71059.tar.gz
[Browser2] Sync from chromium code, including increase target_sdk version to 28 (P) and enable cleartext traffic am: aa6c899803
am: 1bcf52c566 Change-Id: I4804ff2996c55d470a3355ecf51e8d8697ee748f
-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;
}