summaryrefslogtreecommitdiffstats
path: root/android_webview/glue
diff options
context:
space:
mode:
authorMarcin Kosiba <mkosiba@chromium.org>2014-12-11 12:28:01 +0000
committerMarcin Kosiba <mkosiba@chromium.org>2014-12-11 12:29:34 +0000
commit206d4d3e569857c3e8f11e4f8364225688ea8507 (patch)
tree0665388f7010ee31e7b16698f224a9ed15eacb6a /android_webview/glue
parent326e264fa6d71cca3549768b0ccff7f094052fff (diff)
downloadchromium_src-206d4d3e569857c3e8f11e4f8364225688ea8507.zip
chromium_src-206d4d3e569857c3e8f11e4f8364225688ea8507.tar.gz
chromium_src-206d4d3e569857c3e8f11e4f8364225688ea8507.tar.bz2
[android_webview] Move the glue layer into android_webview/glue
This checks in a copy of the WebView glue layer from master-chromium at revision d519b07bd3cb09b571689b00250cb5de977b4dfc. Changes to make the code adhere to the style guide, fix up warnigns and actually switch over to this from the copy in third_party will be made as subsequent CLs. BUG=440792 TEST=None R=benm@chromium.org, rmcilroy@chromium.org Review URL: https://codereview.chromium.org/795033002 Cr-Commit-Position: refs/heads/master@{#307888}
Diffstat (limited to 'android_webview/glue')
-rw-r--r--android_webview/glue/java/DEPS5
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java606
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/CookieManagerAdapter.java148
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java112
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/FileChooserParamsAdapter.java77
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java50
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/GraphicsUtils.java19
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/ResourceRewriter.java28
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/ResourcesContextWrapperFactory.java95
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/UnimplementedWebViewApi.java42
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebBackForwardListChromium.java91
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebHistoryItemChromium.java86
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebIconDatabaseAdapter.java52
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebStorageAdapter.java68
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java2236
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java469
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java1118
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebViewDatabaseAdapter.java57
-rw-r--r--android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java349
19 files changed, 5708 insertions, 0 deletions
diff --git a/android_webview/glue/java/DEPS b/android_webview/glue/java/DEPS
new file mode 100644
index 0000000..794ecd7e
--- /dev/null
+++ b/android_webview/glue/java/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+android_webview/java",
+ "+base/android/java",
+ "+content/public/android/java",
+]
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
new file mode 100644
index 0000000..88f6551
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
@@ -0,0 +1,606 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.util.Log;
+import android.webkit.WebSettings.LayoutAlgorithm;
+import android.webkit.WebSettings.PluginState;
+import android.webkit.WebSettings.RenderPriority;
+import android.webkit.WebSettings.TextSize;
+import android.webkit.WebSettings.ZoomDensity;
+
+import org.chromium.android_webview.AwSettings;
+
+public class ContentSettingsAdapter extends android.webkit.WebSettings {
+
+ private static final String LOGTAG = ContentSettingsAdapter.class.getSimpleName();
+ private static final boolean TRACE = false;
+
+ private AwSettings mAwSettings;
+
+ public ContentSettingsAdapter(AwSettings awSettings) {
+ mAwSettings = awSettings;
+ }
+
+ AwSettings getAwSettings() {
+ return mAwSettings;
+ }
+
+ @Override
+ @Deprecated
+ public void setNavDump(boolean enabled) {
+ // Intentional no-op.
+ }
+
+ @Override
+ @Deprecated
+ public boolean getNavDump() {
+ // Intentional no-op.
+ return false;
+ }
+
+ @Override
+ public void setSupportZoom(boolean support) {
+ if (TRACE) Log.d(LOGTAG, "setSupportZoom=" + support);
+ mAwSettings.setSupportZoom(support);
+ }
+
+ @Override
+ public boolean supportZoom() {
+ return mAwSettings.supportZoom();
+ }
+
+ @Override
+ public void setBuiltInZoomControls(boolean enabled) {
+ if (TRACE) Log.d(LOGTAG, "setBuiltInZoomControls=" + enabled);
+ mAwSettings.setBuiltInZoomControls(enabled);
+ }
+
+ @Override
+ public boolean getBuiltInZoomControls() {
+ return mAwSettings.getBuiltInZoomControls();
+ }
+
+ @Override
+ public void setDisplayZoomControls(boolean enabled) {
+ if (TRACE) Log.d(LOGTAG, "setDisplayZoomControls=" + enabled);
+ mAwSettings.setDisplayZoomControls(enabled);
+ }
+
+ @Override
+ public boolean getDisplayZoomControls() {
+ return mAwSettings.getDisplayZoomControls();
+ }
+
+ @Override
+ public void setAllowFileAccess(boolean allow) {
+ if (TRACE) Log.d(LOGTAG, "setAllowFileAccess=" + allow);
+ mAwSettings.setAllowFileAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowFileAccess() {
+ return mAwSettings.getAllowFileAccess();
+ }
+
+ @Override
+ public void setAllowContentAccess(boolean allow) {
+ if (TRACE) Log.d(LOGTAG, "setAllowContentAccess=" + allow);
+ mAwSettings.setAllowContentAccess(allow);
+ }
+
+ @Override
+ public boolean getAllowContentAccess() {
+ return mAwSettings.getAllowContentAccess();
+ }
+
+ @Override
+ public void setLoadWithOverviewMode(boolean overview) {
+ if (TRACE) Log.d(LOGTAG, "setLoadWithOverviewMode=" + overview);
+ mAwSettings.setLoadWithOverviewMode(overview);
+ }
+
+ @Override
+ public boolean getLoadWithOverviewMode() {
+ return mAwSettings.getLoadWithOverviewMode();
+ }
+
+ @Override
+ public void setAcceptThirdPartyCookies(boolean accept) {
+ if (TRACE) Log.d(LOGTAG, "setAcceptThirdPartyCookies=" + accept);
+ mAwSettings.setAcceptThirdPartyCookies(accept);
+ }
+
+ @Override
+ public boolean getAcceptThirdPartyCookies() {
+ return mAwSettings.getAcceptThirdPartyCookies();
+ }
+
+ @Override
+ public void setEnableSmoothTransition(boolean enable) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public boolean enableSmoothTransition() {
+ // Intentional no-op.
+ return false;
+ }
+
+ @Override
+ public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public boolean getUseWebViewBackgroundForOverscrollBackground() {
+ // Intentional no-op.
+ return false;
+ }
+
+ @Override
+ public void setSaveFormData(boolean save) {
+ if (TRACE) Log.d(LOGTAG, "setSaveFormData=" + save);
+ mAwSettings.setSaveFormData(save);
+ }
+
+ @Override
+ public boolean getSaveFormData() {
+ return mAwSettings.getSaveFormData();
+ }
+
+ @Override
+ public void setSavePassword(boolean save) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public boolean getSavePassword() {
+ // Intentional no-op.
+ return false;
+ }
+
+ @Override
+ public synchronized void setTextZoom(int textZoom) {
+ if (TRACE) Log.d(LOGTAG, "setTextZoom=" + textZoom);
+ mAwSettings.setTextZoom(textZoom);
+ }
+
+ @Override
+ public synchronized int getTextZoom() {
+ return mAwSettings.getTextZoom();
+ }
+
+ @Override
+ public void setDefaultZoom(ZoomDensity zoom) {
+ if (zoom != ZoomDensity.MEDIUM) {
+ Log.w(LOGTAG, "setDefaultZoom not supported, zoom=" + zoom);
+ }
+ }
+
+ @Override
+ public ZoomDensity getDefaultZoom() {
+ // Intentional no-op.
+ return ZoomDensity.MEDIUM;
+ }
+
+ @Override
+ public void setLightTouchEnabled(boolean enabled) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public boolean getLightTouchEnabled() {
+ // Intentional no-op.
+ return false;
+ }
+
+ @Override
+ public synchronized void setUserAgent(int ua) {
+ // Minimal implementation for backwards compatibility: just supports resetting to default.
+ if (ua == 0) {
+ setUserAgentString(null);
+ } else {
+ Log.w(LOGTAG, "setUserAgent not supported, ua=" + ua);
+ }
+ }
+
+ @Override
+ public synchronized int getUserAgent() {
+ // Minimal implementation for backwards compatibility: just identifies default vs custom.
+ return AwSettings.getDefaultUserAgent().equals(getUserAgentString()) ? 0 : -1;
+ }
+
+ @Override
+ public synchronized void setUseWideViewPort(boolean use) {
+ if (TRACE) Log.d(LOGTAG, "setUseWideViewPort=" + use);
+ mAwSettings.setUseWideViewPort(use);
+ }
+
+ @Override
+ public synchronized boolean getUseWideViewPort() {
+ return mAwSettings.getUseWideViewPort();
+ }
+
+ @Override
+ public synchronized void setSupportMultipleWindows(boolean support) {
+ if (TRACE) Log.d(LOGTAG, "setSupportMultipleWindows=" + support);
+ mAwSettings.setSupportMultipleWindows(support);
+ }
+
+ @Override
+ public synchronized boolean supportMultipleWindows() {
+ return mAwSettings.supportMultipleWindows();
+ }
+
+ @Override
+ public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
+ // TODO: Remove the upstream enum and mapping once the new value is in the public API.
+ final AwSettings.LayoutAlgorithm[] chromiumValues = {
+ AwSettings.LayoutAlgorithm.NORMAL,
+ AwSettings.LayoutAlgorithm.SINGLE_COLUMN,
+ AwSettings.LayoutAlgorithm.NARROW_COLUMNS,
+ AwSettings.LayoutAlgorithm.TEXT_AUTOSIZING
+ };
+ mAwSettings.setLayoutAlgorithm(chromiumValues[l.ordinal()]);
+ }
+
+ @Override
+ public synchronized LayoutAlgorithm getLayoutAlgorithm() {
+ // TODO: Remove the upstream enum and mapping once the new value is in the public API.
+ final LayoutAlgorithm[] webViewValues = {
+ LayoutAlgorithm.NORMAL,
+ LayoutAlgorithm.SINGLE_COLUMN,
+ LayoutAlgorithm.NARROW_COLUMNS,
+ LayoutAlgorithm.TEXT_AUTOSIZING
+ };
+ return webViewValues[mAwSettings.getLayoutAlgorithm().ordinal()];
+ }
+
+ @Override
+ public synchronized void setStandardFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setStandardFontFamily=" + font);
+ mAwSettings.setStandardFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getStandardFontFamily() {
+ return mAwSettings.getStandardFontFamily();
+ }
+
+ @Override
+ public synchronized void setFixedFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setFixedFontFamily=" + font);
+ mAwSettings.setFixedFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getFixedFontFamily() {
+ return mAwSettings.getFixedFontFamily();
+ }
+
+ @Override
+ public synchronized void setSansSerifFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setSansSerifFontFamily=" + font);
+ mAwSettings.setSansSerifFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getSansSerifFontFamily() {
+ return mAwSettings.getSansSerifFontFamily();
+ }
+
+ @Override
+ public synchronized void setSerifFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setSerifFontFamily=" + font);
+ mAwSettings.setSerifFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getSerifFontFamily() {
+ return mAwSettings.getSerifFontFamily();
+ }
+
+ @Override
+ public synchronized void setCursiveFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setCursiveFontFamily=" + font);
+ mAwSettings.setCursiveFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getCursiveFontFamily() {
+ return mAwSettings.getCursiveFontFamily();
+ }
+
+ @Override
+ public synchronized void setFantasyFontFamily(String font) {
+ if (TRACE) Log.d(LOGTAG, "setFantasyFontFamily=" + font);
+ mAwSettings.setFantasyFontFamily(font);
+ }
+
+ @Override
+ public synchronized String getFantasyFontFamily() {
+ return mAwSettings.getFantasyFontFamily();
+ }
+
+ @Override
+ public synchronized void setMinimumFontSize(int size) {
+ if (TRACE) Log.d(LOGTAG, "setMinimumFontSize=" + size);
+ mAwSettings.setMinimumFontSize(size);
+ }
+
+ @Override
+ public synchronized int getMinimumFontSize() {
+ return mAwSettings.getMinimumFontSize();
+ }
+
+ @Override
+ public synchronized void setMinimumLogicalFontSize(int size) {
+ if (TRACE) Log.d(LOGTAG, "setMinimumLogicalFontSize=" + size);
+ mAwSettings.setMinimumLogicalFontSize(size);
+ }
+
+ @Override
+ public synchronized int getMinimumLogicalFontSize() {
+ return mAwSettings.getMinimumLogicalFontSize();
+ }
+
+ @Override
+ public synchronized void setDefaultFontSize(int size) {
+ if (TRACE) Log.d(LOGTAG, "setDefaultFontSize=" + size);
+ mAwSettings.setDefaultFontSize(size);
+ }
+
+ @Override
+ public synchronized int getDefaultFontSize() {
+ return mAwSettings.getDefaultFontSize();
+ }
+
+ @Override
+ public synchronized void setDefaultFixedFontSize(int size) {
+ if (TRACE) Log.d(LOGTAG, "setDefaultFixedFontSize=" + size);
+ mAwSettings.setDefaultFixedFontSize(size);
+ }
+
+ @Override
+ public synchronized int getDefaultFixedFontSize() {
+ return mAwSettings.getDefaultFixedFontSize();
+ }
+
+ @Override
+ public synchronized void setLoadsImagesAutomatically(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setLoadsImagesAutomatically=" + flag);
+ mAwSettings.setLoadsImagesAutomatically(flag);
+ }
+
+ @Override
+ public synchronized boolean getLoadsImagesAutomatically() {
+ return mAwSettings.getLoadsImagesAutomatically();
+ }
+
+ @Override
+ public synchronized void setBlockNetworkImage(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setBlockNetworkImage=" + flag);
+ mAwSettings.setImagesEnabled(!flag);
+ }
+
+ @Override
+ public synchronized boolean getBlockNetworkImage() {
+ return !mAwSettings.getImagesEnabled();
+ }
+
+ @Override
+ public synchronized void setBlockNetworkLoads(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setBlockNetworkLoads=" + flag);
+ mAwSettings.setBlockNetworkLoads(flag);
+ }
+
+ @Override
+ public synchronized boolean getBlockNetworkLoads() {
+ return mAwSettings.getBlockNetworkLoads();
+ }
+
+ @Override
+ public synchronized void setJavaScriptEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setJavaScriptEnabled=" + flag);
+ mAwSettings.setJavaScriptEnabled(flag);
+ }
+
+ @Override
+ public void setAllowUniversalAccessFromFileURLs(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setAllowUniversalAccessFromFileURLs=" + flag);
+ mAwSettings.setAllowUniversalAccessFromFileURLs(flag);
+ }
+
+ @Override
+ public void setAllowFileAccessFromFileURLs(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setAllowFileAccessFromFileURLs=" + flag);
+ mAwSettings.setAllowFileAccessFromFileURLs(flag);
+ }
+
+ @Override
+ public synchronized void setPluginsEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setPluginsEnabled=" + flag);
+ mAwSettings.setPluginsEnabled(flag);
+ }
+
+ @Override
+ public synchronized void setPluginState(PluginState state) {
+ if (TRACE) Log.d(LOGTAG, "setPluginState=" + state);
+ mAwSettings.setPluginState(state);
+ }
+
+ @Override
+ public synchronized void setDatabasePath(String databasePath) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public synchronized void setGeolocationDatabasePath(String databasePath) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public synchronized void setAppCacheEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setAppCacheEnabled=" + flag);
+ mAwSettings.setAppCacheEnabled(flag);
+ }
+
+ @Override
+ public synchronized void setAppCachePath(String appCachePath) {
+ if (TRACE) Log.d(LOGTAG, "setAppCachePath=" + appCachePath);
+ mAwSettings.setAppCachePath(appCachePath);
+ }
+
+ @Override
+ public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public synchronized void setDatabaseEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setDatabaseEnabled=" + flag);
+ mAwSettings.setDatabaseEnabled(flag);
+ }
+
+ @Override
+ public synchronized void setDomStorageEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setDomStorageEnabled=" + flag);
+ mAwSettings.setDomStorageEnabled(flag);
+ }
+
+ @Override
+ public synchronized boolean getDomStorageEnabled() {
+ return mAwSettings.getDomStorageEnabled();
+ }
+
+ @Override
+ public synchronized String getDatabasePath() {
+ // Intentional no-op.
+ return "";
+ }
+
+ @Override
+ public synchronized boolean getDatabaseEnabled() {
+ return mAwSettings.getDatabaseEnabled();
+ }
+
+ @Override
+ public synchronized void setGeolocationEnabled(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setGeolocationEnabled=" + flag);
+ mAwSettings.setGeolocationEnabled(flag);
+ }
+
+ @Override
+ public synchronized boolean getJavaScriptEnabled() {
+ return mAwSettings.getJavaScriptEnabled();
+ }
+
+ @Override
+ public boolean getAllowUniversalAccessFromFileURLs() {
+ return mAwSettings.getAllowUniversalAccessFromFileURLs();
+ }
+
+ @Override
+ public boolean getAllowFileAccessFromFileURLs() {
+ return mAwSettings.getAllowFileAccessFromFileURLs();
+ }
+
+ @Override
+ public synchronized boolean getPluginsEnabled() {
+ return mAwSettings.getPluginsEnabled();
+ }
+
+ @Override
+ public synchronized PluginState getPluginState() {
+ return mAwSettings.getPluginState();
+ }
+
+ @Override
+ public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setJavaScriptCanOpenWindowsAutomatically=" + flag);
+ mAwSettings.setJavaScriptCanOpenWindowsAutomatically(flag);
+ }
+
+ @Override
+ public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
+ return mAwSettings.getJavaScriptCanOpenWindowsAutomatically();
+ }
+
+ @Override
+ public synchronized void setDefaultTextEncodingName(String encoding) {
+ if (TRACE) Log.d(LOGTAG, "setDefaultTextEncodingName=" + encoding);
+ mAwSettings.setDefaultTextEncodingName(encoding);
+ }
+
+ @Override
+ public synchronized String getDefaultTextEncodingName() {
+ return mAwSettings.getDefaultTextEncodingName();
+ }
+
+ @Override
+ public synchronized void setUserAgentString(String ua) {
+ if (TRACE) Log.d(LOGTAG, "setUserAgentString=" + ua);
+ mAwSettings.setUserAgentString(ua);
+ }
+
+ @Override
+ public synchronized String getUserAgentString() {
+ return mAwSettings.getUserAgentString();
+ }
+
+ @Override
+ public void setNeedInitialFocus(boolean flag) {
+ if (TRACE) Log.d(LOGTAG, "setNeedInitialFocus=" + flag);
+ mAwSettings.setShouldFocusFirstNode(flag);
+ }
+
+ @Override
+ public synchronized void setRenderPriority(RenderPriority priority) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void setCacheMode(int mode) {
+ if (TRACE) Log.d(LOGTAG, "setCacheMode=" + mode);
+ mAwSettings.setCacheMode(mode);
+ }
+
+ @Override
+ public int getCacheMode() {
+ return mAwSettings.getCacheMode();
+ }
+
+ @Override
+ public void setMediaPlaybackRequiresUserGesture(boolean require) {
+ if (TRACE) Log.d(LOGTAG, "setMediaPlaybackRequiresUserGesture=" + require);
+ mAwSettings.setMediaPlaybackRequiresUserGesture(require);
+ }
+
+ @Override
+ public boolean getMediaPlaybackRequiresUserGesture() {
+ return mAwSettings.getMediaPlaybackRequiresUserGesture();
+ }
+
+// @Override
+ public void setMixedContentMode(int mode) {
+ mAwSettings.setMixedContentMode(mode);
+ }
+
+// @Override
+ public int getMixedContentMode() {
+ return mAwSettings.getMixedContentMode();
+ }
+
+// @Override
+ public void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag) {
+ mAwSettings.setVideoOverlayForEmbeddedVideoEnabled(flag);
+ }
+
+// @Override
+ public boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled() {
+ return mAwSettings.getVideoOverlayForEmbeddedVideoEnabled();
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/CookieManagerAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/CookieManagerAdapter.java
new file mode 100644
index 0000000..547184a
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/CookieManagerAdapter.java
@@ -0,0 +1,148 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.net.ParseException;
+import android.net.WebAddress;
+import android.util.Log;
+import android.webkit.CookieManager;
+import android.webkit.ValueCallback;
+import android.webkit.WebView;
+
+import org.chromium.android_webview.AwCookieManager;
+
+public class CookieManagerAdapter extends CookieManager {
+
+ private static final String LOGTAG = "CookieManager";
+
+ AwCookieManager mChromeCookieManager;
+
+ public CookieManagerAdapter(AwCookieManager chromeCookieManager) {
+ mChromeCookieManager = chromeCookieManager;
+ }
+
+ @Override
+ public synchronized void setAcceptCookie(boolean accept) {
+ mChromeCookieManager.setAcceptCookie(accept);
+ }
+
+ @Override
+ public synchronized boolean acceptCookie() {
+ return mChromeCookieManager.acceptCookie();
+ }
+
+ @Override
+ public synchronized void setAcceptThirdPartyCookies(WebView webView, boolean accept) {
+ webView.getSettings().setAcceptThirdPartyCookies(accept);
+ }
+
+ @Override
+ public synchronized boolean acceptThirdPartyCookies(WebView webView) {
+ return webView.getSettings().getAcceptThirdPartyCookies();
+ }
+
+ @Override
+ public void setCookie(String url, String value) {
+ try {
+ mChromeCookieManager.setCookie(fixupUrl(url), value);
+ } catch (ParseException e) {
+ Log.e(LOGTAG, "Not setting cookie due to error parsing URL: " + url, e);
+ }
+ }
+
+ @Override
+ public void setCookie(String url, String value, ValueCallback<Boolean> callback) {
+ try {
+ mChromeCookieManager.setCookie(fixupUrl(url), value, callback);
+ } catch (ParseException e) {
+ Log.e(LOGTAG, "Not setting cookie due to error parsing URL: " + url, e);
+ }
+ }
+
+ @Override
+ public String getCookie(String url) {
+ try {
+ return mChromeCookieManager.getCookie(fixupUrl(url));
+ } catch (ParseException e) {
+ Log.e(LOGTAG, "Unable to get cookies due to error parsing URL: " + url, e);
+ return null;
+ }
+ }
+
+ @Override
+ public String getCookie(String url, boolean privateBrowsing) {
+ return getCookie(url);
+ }
+
+ // TODO(igsolla): remove this override once the WebView apk does not longer need
+ // to be binary compatibility with the API 21 version of the framework
+ /**
+ * IMPORTANT: This override is required for compatibility with the API 21 version of
+ * {@link CookieManager}.
+ */
+ @Override
+ public synchronized String getCookie(WebAddress uri) {
+ return mChromeCookieManager.getCookie(uri.toString());
+ }
+
+ @Override
+ public void removeSessionCookie() {
+ mChromeCookieManager.removeSessionCookies();
+ }
+
+ @Override
+ public void removeSessionCookies(ValueCallback<Boolean> callback) {
+ mChromeCookieManager.removeSessionCookies(callback);
+ }
+
+ @Override
+ public void removeAllCookie() {
+ mChromeCookieManager.removeAllCookies();
+ }
+
+ @Override
+ public void removeAllCookies(ValueCallback<Boolean> callback) {
+ mChromeCookieManager.removeAllCookies(callback);
+ }
+
+ @Override
+ public synchronized boolean hasCookies() {
+ return mChromeCookieManager.hasCookies();
+ }
+
+ @Override
+ public synchronized boolean hasCookies(boolean privateBrowsing) {
+ return mChromeCookieManager.hasCookies();
+ }
+
+ @Override
+ public void removeExpiredCookie() {
+ mChromeCookieManager.removeExpiredCookie();
+ }
+
+ @Override
+ public void flush() {
+ mChromeCookieManager.flushCookieStore();
+ }
+
+ @Override
+ protected boolean allowFileSchemeCookiesImpl() {
+ return mChromeCookieManager.allowFileSchemeCookies();
+ }
+
+ @Override
+ protected void setAcceptFileSchemeCookiesImpl(boolean accept) {
+ mChromeCookieManager.setAcceptFileSchemeCookies(accept);
+ }
+
+ private static String fixupUrl(String url) throws ParseException {
+ // WebAddress is a private API in the android framework and a "quirk"
+ // of the Classic WebView implementation that allowed embedders to
+ // be relaxed about what URLs they passed into the CookieManager, so we
+ // do the same normalisation before entering the chromium stack.
+ return new WebAddress(url).toString();
+ }
+
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java b/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java
new file mode 100644
index 0000000..c27287d
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/DrawGLFunctor.java
@@ -0,0 +1,112 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.view.View;
+import android.graphics.Canvas;
+import android.util.Log;
+
+import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate;
+
+import org.chromium.content.common.CleanupReference;
+
+// Simple Java abstraction and wrapper for the native DrawGLFunctor flow.
+// An instance of this class can be constructed, bound to a single view context (i.e. AwContennts)
+// and then drawn and detached from the view tree any number of times (using requestDrawGL and
+// detach respectively). Then when finished with, it can be explicitly released by calling
+// destroy() or will clean itself up as required via finalizer / CleanupReference.
+class DrawGLFunctor {
+
+ private static final String TAG = DrawGLFunctor.class.getSimpleName();
+
+ // Pointer to native side instance
+ private CleanupReference mCleanupReference;
+ private DestroyRunnable mDestroyRunnable;
+ private WebViewDelegate mWebViewDelegate;
+
+ public DrawGLFunctor(long viewContext, WebViewDelegate webViewDelegate) {
+ mDestroyRunnable = new DestroyRunnable(nativeCreateGLFunctor(viewContext), webViewDelegate);
+ mCleanupReference = new CleanupReference(this, mDestroyRunnable);
+ mWebViewDelegate = webViewDelegate;
+ }
+
+ public void destroy() {
+ detach();
+ if (mCleanupReference != null) {
+ mCleanupReference.cleanupNow();
+ mCleanupReference = null;
+ mDestroyRunnable = null;
+ mWebViewDelegate = null;
+ }
+ }
+
+ public void detach() {
+ mDestroyRunnable.detachNativeFunctor();
+ }
+
+ public boolean requestDrawGL(Canvas canvas, View containerView,
+ boolean waitForCompletion) {
+ if (mDestroyRunnable.mNativeDrawGLFunctor == 0) {
+ throw new RuntimeException("requested DrawGL on already destroyed DrawGLFunctor");
+ }
+
+ if (canvas != null && waitForCompletion) {
+ throw new IllegalArgumentException("requested a blocking DrawGL with a not null canvas.");
+ }
+
+ if (!mWebViewDelegate.canInvokeDrawGlFunctor(containerView)) {
+ return false;
+ }
+
+ mDestroyRunnable.mContainerView = containerView;
+
+ if (canvas == null) {
+ mWebViewDelegate.invokeDrawGlFunctor(containerView,
+ mDestroyRunnable.mNativeDrawGLFunctor, waitForCompletion);
+ return true;
+ }
+
+ mWebViewDelegate.callDrawGlFunction(canvas, mDestroyRunnable.mNativeDrawGLFunctor);
+ return true;
+ }
+
+ public static void setChromiumAwDrawGLFunction(long functionPointer) {
+ nativeSetChromiumAwDrawGLFunction(functionPointer);
+ }
+
+ // Holds the core resources of the class, everything required to correctly cleanup.
+ // IMPORTANT: this class must not hold any reference back to the outer DrawGLFunctor
+ // instance, as that will defeat GC of that object.
+ private static final class DestroyRunnable implements Runnable {
+ private WebViewDelegate mWebViewDelegate;
+ View mContainerView;
+ long mNativeDrawGLFunctor;
+ DestroyRunnable(long nativeDrawGLFunctor, WebViewDelegate webViewDelegate) {
+ mNativeDrawGLFunctor = nativeDrawGLFunctor;
+ mWebViewDelegate = webViewDelegate;
+ }
+
+ // Called when the outer DrawGLFunctor instance has been GC'ed, i.e this is its finalizer.
+ @Override
+ public void run() {
+ detachNativeFunctor();
+ nativeDestroyGLFunctor(mNativeDrawGLFunctor);
+ mNativeDrawGLFunctor = 0;
+ }
+
+ void detachNativeFunctor() {
+ if (mNativeDrawGLFunctor != 0 && mContainerView != null
+ && mWebViewDelegate != null) {
+ mWebViewDelegate.detachDrawGlFunctor(mContainerView, mNativeDrawGLFunctor);
+ }
+ mContainerView = null;
+ mWebViewDelegate = null;
+ }
+ }
+
+ private static native long nativeCreateGLFunctor(long viewContext);
+ private static native void nativeDestroyGLFunctor(long functor);
+ private static native void nativeSetChromiumAwDrawGLFunction(long functionPointer);
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/FileChooserParamsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/FileChooserParamsAdapter.java
new file mode 100644
index 0000000..fe7ed55
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/FileChooserParamsAdapter.java
@@ -0,0 +1,77 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.webkit.WebChromeClient.FileChooserParams;
+
+import org.chromium.android_webview.AwContentsClient;
+
+public class FileChooserParamsAdapter extends FileChooserParams {
+ private AwContentsClient.FileChooserParams mParams;
+
+ public static Uri[] parseFileChooserResult(int resultCode, Intent intent) {
+ if (resultCode == Activity.RESULT_CANCELED) {
+ return null;
+ }
+ Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
+ : intent.getData();
+
+ Uri[] uris = null;
+ if (result != null) {
+ uris = new Uri[1];
+ uris[0] = result;
+ }
+ return uris;
+ }
+
+ FileChooserParamsAdapter(AwContentsClient.FileChooserParams params, Context context) {
+ mParams = params;
+ }
+
+ @Override
+ public int getMode() {
+ return mParams.mode;
+ }
+
+ @Override
+ public String[] getAcceptTypes() {
+ if (mParams.acceptTypes == null)
+ return new String[0];
+ return mParams.acceptTypes.split(";");
+ }
+
+ @Override
+ public boolean isCaptureEnabled() {
+ return mParams.capture;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mParams.title;
+ }
+
+ @Override
+ public String getFilenameHint() {
+ return mParams.defaultFilename;
+ }
+
+ @Override
+ public Intent createIntent() {
+ // TODO: Move this code to Aw. Once code is moved
+ // and merged to M37 get rid of this.
+ String mimeType = "*/*";
+ if (mParams.acceptTypes != null && !mParams.acceptTypes.trim().isEmpty())
+ mimeType = mParams.acceptTypes.split(";")[0];
+
+ Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+ i.addCategory(Intent.CATEGORY_OPENABLE);
+ i.setType(mimeType);
+ return i;
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java
new file mode 100644
index 0000000..6249906
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/GeolocationPermissionsAdapter.java
@@ -0,0 +1,50 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.webkit.GeolocationPermissions;
+import android.webkit.ValueCallback;
+
+import org.chromium.android_webview.AwGeolocationPermissions;
+
+import java.util.Set;
+
+/**
+ * Chromium implementation of GeolocationPermissions -- forwards calls to the
+ * chromium internal implementation.
+ */
+final class GeolocationPermissionsAdapter extends GeolocationPermissions {
+
+ private AwGeolocationPermissions mChromeGeolocationPermissions;
+
+ public GeolocationPermissionsAdapter(AwGeolocationPermissions chromeGeolocationPermissions) {
+ mChromeGeolocationPermissions = chromeGeolocationPermissions;
+ }
+
+ @Override
+ public void allow(String origin) {
+ mChromeGeolocationPermissions.allow(origin);
+ }
+
+ @Override
+ public void clear(String origin) {
+ mChromeGeolocationPermissions.clear(origin);
+ }
+
+ @Override
+ public void clearAll() {
+ mChromeGeolocationPermissions.clearAll();
+ }
+
+ @Override
+ public void getAllowed(String origin, ValueCallback<Boolean> callback) {
+ mChromeGeolocationPermissions.getAllowed(origin, callback);
+ }
+
+ @Override
+ public void getOrigins(ValueCallback<Set<String>> callback) {
+ mChromeGeolocationPermissions.getOrigins(callback);
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/GraphicsUtils.java b/android_webview/glue/java/src/com/android/webview/chromium/GraphicsUtils.java
new file mode 100644
index 0000000..20940db
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/GraphicsUtils.java
@@ -0,0 +1,19 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+abstract class GraphicsUtils {
+ public static long getDrawSWFunctionTable() {
+ return nativeGetDrawSWFunctionTable();
+ }
+
+ public static long getDrawGLFunctionTable() {
+ return nativeGetDrawGLFunctionTable();
+ }
+
+ private static native long nativeGetDrawSWFunctionTable();
+ private static native long nativeGetDrawGLFunctionTable();
+
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ResourceRewriter.java b/android_webview/glue/java/src/com/android/webview/chromium/ResourceRewriter.java
new file mode 100644
index 0000000..bee5020
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ResourceRewriter.java
@@ -0,0 +1,28 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.Context;
+
+/**
+ * Helper class used to fix up resource ids.
+ * This is mostly a copy of the code in frameworks/base/core/java/android/app/LoadedApk.java.
+ * TODO: Remove if a cleaner mechanism is provided (either public API or AAPT is changed to generate
+ * this code).
+ */
+class ResourceRewriter {
+
+ /**
+ * Rewrite the R 'constants' for the WebView library apk.
+ */
+ public static void rewriteRValues(final int packageId) {
+ // TODO: We should use jarjar to remove the redundant R classes here, but due
+ // to a bug in jarjar it's not possible to rename classes with '$' in their name.
+ // See b/15684775.
+ com.android.webview.chromium.R.onResourcesLoaded(packageId);
+ org.chromium.ui.R.onResourcesLoaded(packageId);
+ org.chromium.content.R.onResourcesLoaded(packageId);
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ResourcesContextWrapperFactory.java b/android_webview/glue/java/src/com/android/webview/chromium/ResourcesContextWrapperFactory.java
new file mode 100644
index 0000000..29b5bd9
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ResourcesContextWrapperFactory.java
@@ -0,0 +1,95 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+
+import java.util.WeakHashMap;
+
+/**
+ * This class allows us to wrap the application context so that the WebView implementation can
+ * correctly reference both org.chromium.* and application classes which is necessary to properly
+ * inflate UI. We keep a weak map from contexts to wrapped contexts to avoid constantly re-wrapping
+ * or doubly wrapping contexts.
+ */
+public class ResourcesContextWrapperFactory {
+ private static WeakHashMap<Context,ContextWrapper> sCtxToWrapper
+ = new WeakHashMap<Context,ContextWrapper>();
+ private static final Object sLock = new Object();
+
+ private ResourcesContextWrapperFactory() {
+ }
+
+ public static Context get(Context ctx) {
+ ContextWrapper wrappedCtx;
+ synchronized (sLock) {
+ wrappedCtx = sCtxToWrapper.get(ctx);
+ if (wrappedCtx == null) {
+ wrappedCtx = createWrapper(ctx);
+ sCtxToWrapper.put(ctx, wrappedCtx);
+ }
+ }
+ return wrappedCtx;
+ }
+
+ private static ContextWrapper createWrapper(final Context ctx) {
+ return new ContextWrapper(ctx) {
+ private Context applicationContext;
+
+ @Override
+ public ClassLoader getClassLoader() {
+ final ClassLoader appCl = getBaseContext().getClassLoader();
+ final ClassLoader webViewCl = this.getClass().getClassLoader();
+ return new ClassLoader() {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ // First look in the WebViewProvider class loader.
+ try {
+ return webViewCl.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ // Look in the app class loader; allowing it to throw ClassNotFoundException.
+ return appCl.loadClass(name);
+ }
+ }
+ };
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
+ LayoutInflater i = (LayoutInflater) getBaseContext().getSystemService(name);
+ return i.cloneInContext(this);
+ } else {
+ return getBaseContext().getSystemService(name);
+ }
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ if (applicationContext == null)
+ applicationContext = get(ctx.getApplicationContext());
+ return applicationContext;
+ }
+
+ @Override
+ public void registerComponentCallbacks(ComponentCallbacks callback) {
+ // We have to override registerComponentCallbacks and unregisterComponentCallbacks
+ // since they call getApplicationContext().[un]registerComponentCallbacks()
+ // which causes us to go into a loop.
+ ctx.registerComponentCallbacks(callback);
+ }
+
+ @Override
+ public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+ ctx.unregisterComponentCallbacks(callback);
+ }
+ };
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/UnimplementedWebViewApi.java b/android_webview/glue/java/src/com/android/webview/chromium/UnimplementedWebViewApi.java
new file mode 100644
index 0000000..39372d3
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/UnimplementedWebViewApi.java
@@ -0,0 +1,42 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.util.Log;
+
+// TODO: remove this when all WebView APIs have been implemented.
+public class UnimplementedWebViewApi {
+ private static String TAG = "UnimplementedWebViewApi";
+
+ private static class UnimplementedWebViewApiException extends UnsupportedOperationException {
+ public UnimplementedWebViewApiException() {
+ super();
+ }
+ }
+
+ private static boolean THROW = false;
+ // By default we keep the traces down to one frame to reduce noise, but for debugging it might
+ // be useful to set this to true.
+ private static boolean FULL_TRACE = false;
+
+ public static void invoke() throws UnimplementedWebViewApiException {
+ if (THROW) {
+ throw new UnimplementedWebViewApiException();
+ } else {
+ if (FULL_TRACE) {
+ Log.w(TAG, "Unimplemented WebView method called in: " +
+ Log.getStackTraceString(new Throwable()));
+ } else {
+ StackTraceElement[] trace = new Throwable().getStackTrace();
+ // The stack trace [0] index is this method (invoke()).
+ StackTraceElement unimplementedMethod = trace[1];
+ StackTraceElement caller = trace[2];
+ Log.w(TAG, "Unimplemented WebView method " + unimplementedMethod.getMethodName() +
+ " called from: " + caller.toString());
+ }
+ }
+ }
+
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebBackForwardListChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebBackForwardListChromium.java
new file mode 100644
index 0000000..5e29773
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebBackForwardListChromium.java
@@ -0,0 +1,91 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import org.chromium.content_public.browser.NavigationHistory;
+
+import android.webkit.WebBackForwardList;
+import android.webkit.WebHistoryItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * WebView Chromium implementation of WebBackForwardList. Simple immutable
+ * wrapper around NavigationHistory.
+ */
+public class WebBackForwardListChromium extends WebBackForwardList {
+ private final List<WebHistoryItemChromium> mHistroryItemList;
+ private final int mCurrentIndex;
+
+ /* package */ WebBackForwardListChromium(NavigationHistory nav_history) {
+ mCurrentIndex = nav_history.getCurrentEntryIndex();
+ mHistroryItemList = new ArrayList<WebHistoryItemChromium>(nav_history.getEntryCount());
+ for (int i = 0; i < nav_history.getEntryCount(); ++i) {
+ mHistroryItemList.add(
+ new WebHistoryItemChromium(nav_history.getEntryAtIndex(i)));
+ }
+ }
+
+ /**
+ * See {@link android.webkit.WebBackForwardList#getCurrentItem}.
+ */
+ @Override
+ public synchronized WebHistoryItem getCurrentItem() {
+ if (getSize() == 0) {
+ return null;
+ } else {
+ return getItemAtIndex(getCurrentIndex());
+ }
+ }
+
+ /**
+ * See {@link android.webkit.WebBackForwardList#getCurrentIndex}.
+ */
+ @Override
+ public synchronized int getCurrentIndex() {
+ return mCurrentIndex;
+ }
+
+ /**
+ * See {@link android.webkit.WebBackForwardList#getItemAtIndex}.
+ */
+ @Override
+ public synchronized WebHistoryItem getItemAtIndex(int index) {
+ if (index < 0 || index >= getSize()) {
+ return null;
+ } else {
+ return mHistroryItemList.get(index);
+ }
+ }
+
+ /**
+ * See {@link android.webkit.WebBackForwardList#getSize}.
+ */
+ @Override
+ public synchronized int getSize() {
+ return mHistroryItemList.size();
+ }
+
+ // Clone constructor.
+ private WebBackForwardListChromium(List<WebHistoryItemChromium> list,
+ int currentIndex) {
+ mHistroryItemList = list;
+ mCurrentIndex = currentIndex;
+ }
+
+ /**
+ * See {@link android.webkit.WebBackForwardList#clone}.
+ */
+ @Override
+ protected synchronized WebBackForwardListChromium clone() {
+ List<WebHistoryItemChromium> list =
+ new ArrayList<WebHistoryItemChromium>(getSize());
+ for (int i = 0; i < getSize(); ++i) {
+ list.add(mHistroryItemList.get(i).clone());
+ }
+ return new WebBackForwardListChromium(list, mCurrentIndex);
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebHistoryItemChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebHistoryItemChromium.java
new file mode 100644
index 0000000..b499414
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebHistoryItemChromium.java
@@ -0,0 +1,86 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import org.chromium.content_public.browser.NavigationEntry;
+
+import android.graphics.Bitmap;
+import android.webkit.WebHistoryItem;
+
+/**
+ * WebView Chromium implementation of WebHistoryItem. Simple immutable wrapper
+ * around NavigationEntry
+ */
+public class WebHistoryItemChromium extends WebHistoryItem {
+ private final String mUrl;
+ private final String mOriginalUrl;
+ private final String mTitle;
+ private final Bitmap mFavicon;
+
+ /* package */ WebHistoryItemChromium(NavigationEntry entry) {
+ mUrl = entry.getUrl();
+ mOriginalUrl = entry.getOriginalUrl();
+ mTitle = entry.getTitle();
+ mFavicon = entry.getFavicon();
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#getId}.
+ */
+ @Override
+ public int getId() {
+ // This method is deprecated in superclass. Returning constant -1 now.
+ return -1;
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#getUrl}.
+ */
+ @Override
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#getOriginalUrl}.
+ */
+ @Override
+ public String getOriginalUrl() {
+ return mOriginalUrl;
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#getTitle}.
+ */
+ @Override
+ public String getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#getFavicon}.
+ */
+ @Override
+ public Bitmap getFavicon() {
+ return mFavicon;
+ }
+
+ // Clone constructor.
+ private WebHistoryItemChromium(
+ String url, String originalUrl, String title, Bitmap favicon) {
+ mUrl = url;
+ mOriginalUrl = originalUrl;
+ mTitle = title;
+ mFavicon = favicon;
+ }
+
+ /**
+ * See {@link android.webkit.WebHistoryItem#clone}.
+ */
+ @Override
+ public synchronized WebHistoryItemChromium clone() {
+ return new WebHistoryItemChromium(mUrl, mOriginalUrl, mTitle, mFavicon);
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebIconDatabaseAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebIconDatabaseAdapter.java
new file mode 100644
index 0000000..1c970d1
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebIconDatabaseAdapter.java
@@ -0,0 +1,52 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.ContentResolver;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebIconDatabase.IconListener;
+
+import org.chromium.android_webview.AwContents;
+
+/**
+ * Chromium implementation of WebIconDatabase -- big old no-op (base class is deprecated).
+ */
+final class WebIconDatabaseAdapter extends WebIconDatabase {
+ @Override
+ public void open(String path) {
+ AwContents.setShouldDownloadFavicons();
+ }
+
+ @Override
+ public void close() {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void removeAllIcons() {
+ // Intentional no-op: we have no database so nothing to remove.
+ }
+
+ @Override
+ public void requestIconForPageUrl(String url, IconListener listener) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void bulkRequestIconForPageUrl(ContentResolver cr, String where,
+ IconListener listener) {
+ // Intentional no-op: hidden in base class.
+ }
+
+ @Override
+ public void retainIconForPageUrl(String url) {
+ // Intentional no-op.
+ }
+
+ @Override
+ public void releaseIconForPageUrl(String url) {
+ // Intentional no-op.
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebStorageAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebStorageAdapter.java
new file mode 100644
index 0000000..9d36fb4
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebStorageAdapter.java
@@ -0,0 +1,68 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.webkit.ValueCallback;
+import android.webkit.WebStorage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.android_webview.AwQuotaManagerBridge;
+
+/**
+ * Chromium implementation of WebStorage -- forwards calls to the
+ * chromium internal implementation.
+ */
+final class WebStorageAdapter extends WebStorage {
+ private final AwQuotaManagerBridge mQuotaManagerBridge;
+ WebStorageAdapter(AwQuotaManagerBridge quotaManagerBridge) {
+ mQuotaManagerBridge = quotaManagerBridge;
+ }
+
+ @Override
+ public void getOrigins(final ValueCallback<Map> callback) {
+ mQuotaManagerBridge.getOrigins(new ValueCallback<AwQuotaManagerBridge.Origins>() {
+ @Override
+ public void onReceiveValue(AwQuotaManagerBridge.Origins origins) {
+ Map<String, Origin> originsMap = new HashMap<String, Origin>();
+ for (int i = 0; i < origins.mOrigins.length; ++i) {
+ Origin origin = new Origin(origins.mOrigins[i], origins.mQuotas[i],
+ origins.mUsages[i]) {
+ // Intentionally empty to work around cross-package protected visibility
+ // of Origin constructor.
+ };
+ originsMap.put(origins.mOrigins[i], origin);
+ }
+ callback.onReceiveValue(originsMap);
+ }
+ });
+ }
+
+ @Override
+ public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
+ mQuotaManagerBridge.getUsageForOrigin(origin, callback);
+ }
+
+ @Override
+ public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
+ mQuotaManagerBridge.getQuotaForOrigin(origin, callback);
+ }
+
+ @Override
+ public void setQuotaForOrigin(String origin, long quota) {
+ // Intentional no-op for deprecated method.
+ }
+
+ @Override
+ public void deleteOrigin(String origin) {
+ mQuotaManagerBridge.deleteOrigin(origin);
+ }
+
+ @Override
+ public void deleteAllData() {
+ mQuotaManagerBridge.deleteAllData();
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
new file mode 100644
index 0000000..55fe5c8
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -0,0 +1,2236 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.http.SslCertificate;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.Handler;
+import android.os.Message;
+import android.print.PrintDocumentAdapter;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.webkit.DownloadListener;
+import android.webkit.FindActionModeCallback;
+import android.webkit.JavascriptInterface;
+import android.webkit.ValueCallback;
+import android.webkit.WebBackForwardList;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProvider;
+import android.webkit.WebChromeClient.CustomViewCallback;
+import android.widget.TextView;
+
+import org.chromium.android_webview.AwBrowserContext;
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsStatics;
+import org.chromium.android_webview.AwLayoutSizer;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.AwPrintDocumentAdapter;
+import org.chromium.base.ThreadUtils;
+import org.chromium.content.browser.SmartClipProvider;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.net.NetworkChangeNotifier;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+
+/**
+ * This class is the delegate to which WebViewProxy forwards all API calls.
+ *
+ * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
+ * it). This class also contains WebView-specific APIs that require the creation of other
+ * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
+ * and a small set of no-op deprecated APIs.
+ */
+class WebViewChromium implements WebViewProvider,
+ WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate, SmartClipProvider {
+
+ private class WebViewChromiumRunQueue {
+ public WebViewChromiumRunQueue() {
+ mQueue = new ConcurrentLinkedQueue<Runnable>();
+ }
+
+ public void addTask(Runnable task) {
+ mQueue.add(task);
+ if (mFactory.hasStarted()) {
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ drainQueue();
+ }
+ });
+ }
+ }
+
+ public void drainQueue() {
+ if (mQueue == null || mQueue.isEmpty()) {
+ return;
+ }
+
+ Runnable task = mQueue.poll();
+ while(task != null) {
+ task.run();
+ task = mQueue.poll();
+ }
+ }
+
+ private Queue<Runnable> mQueue;
+ }
+
+ private WebViewChromiumRunQueue mRunQueue;
+
+ private static final String TAG = WebViewChromium.class.getSimpleName();
+
+ // The WebView that this WebViewChromium is the provider for.
+ WebView mWebView;
+ // Lets us access protected View-derived methods on the WebView instance we're backing.
+ WebView.PrivateAccess mWebViewPrivate;
+ // The client adapter class.
+ private WebViewContentsClientAdapter mContentsClientAdapter;
+ // The wrapped Context.
+ private Context mContext;
+
+ // Variables for functionality provided by this adapter ---------------------------------------
+ private ContentSettingsAdapter mWebSettings;
+ // The WebView wrapper for ContentViewCore and required browser compontents.
+ private AwContents mAwContents;
+ // Non-null if this webview is using the GL accelerated draw path.
+ private DrawGLFunctor mGLfunctor;
+
+ private final WebView.HitTestResult mHitTestResult;
+
+ private final int mAppTargetSdkVersion;
+
+ private WebViewChromiumFactoryProvider mFactory;
+
+ private static boolean sRecordWholeDocumentEnabledByApi = false;
+ static void enableSlowWholeDocumentDraw() {
+ sRecordWholeDocumentEnabledByApi = true;
+ }
+
+ // This does not touch any global / non-threadsafe state, but note that
+ // init is ofter called right after and is NOT threadsafe.
+ public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
+ WebView.PrivateAccess webViewPrivate) {
+ mWebView = webView;
+ mWebViewPrivate = webViewPrivate;
+ mHitTestResult = new WebView.HitTestResult();
+ mContext = ResourcesContextWrapperFactory.get(mWebView.getContext());
+ mAppTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ mFactory = factory;
+ mRunQueue = new WebViewChromiumRunQueue();
+ factory.getWebViewDelegate().addWebViewAssetPath(mWebView.getContext());
+ }
+
+ static void completeWindowCreation(WebView parent, WebView child) {
+ AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
+ AwContents childContents =
+ child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
+ parentContents.supplyContentsForPopup(childContents);
+ }
+
+ private <T> T runBlockingFuture(FutureTask<T> task) {
+ if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!");
+ if (ThreadUtils.runningOnUiThread()) {
+ throw new IllegalStateException("This method should only be called off the UI thread");
+ }
+ mRunQueue.addTask(task);
+ try {
+ return task.get(4, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new RuntimeException("Probable deadlock detected due to WebView API being called "
+ + "on incorrect thread while the UI thread is blocked.", e);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
+ // deadlocks.
+ // Do not call this method while on the UI thread!
+ private void runVoidTaskOnUiThreadBlocking(Runnable r) {
+ FutureTask<Void> task = new FutureTask<Void>(r, null);
+ runBlockingFuture(task);
+ }
+
+ private <T> T runOnUiThreadBlocking(Callable<T> c) {
+ return runBlockingFuture(new FutureTask<T>(c));
+ }
+
+ // WebViewProvider methods --------------------------------------------------------------------
+
+ @Override
+ // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
+ // so is ignored. TODO: remove it from WebViewProvider.
+ public void init(final Map<String, Object> javaScriptInterfaces,
+ final boolean privateBrowsing) {
+ if (privateBrowsing) {
+ mFactory.startYourEngines(true);
+ final String msg = "Private browsing is not supported in WebView.";
+ if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
+ throw new IllegalArgumentException(msg);
+ } else {
+ Log.w(TAG, msg);
+ TextView warningLabel = new TextView(mContext);
+ warningLabel.setText(mContext.getString(
+ R.string.webviewchromium_private_browsing_warning));
+ mWebView.addView(warningLabel);
+ }
+ }
+
+ // We will defer real initialization until we know which thread to do it on, unless:
+ // - we are on the main thread already (common case),
+ // - the app is targeting >= JB MR2, in which case checkThread enforces that all usage
+ // comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
+ if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ mFactory.startYourEngines(false);
+ checkThread();
+ } else if (!mFactory.hasStarted()) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ mFactory.startYourEngines(true);
+ }
+ }
+
+ final boolean isAccessFromFileURLsGrantedByDefault =
+ mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
+ final boolean areLegacyQuirksEnabled =
+ mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
+
+ mContentsClientAdapter = new WebViewContentsClientAdapter(
+ mWebView, mContext, mFactory.getWebViewDelegate());
+ mWebSettings = new ContentSettingsAdapter(new AwSettings(
+ mContext, isAccessFromFileURLsGrantedByDefault,
+ areLegacyQuirksEnabled));
+
+ if (mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+ // Prior to Lollipop we always allowed third party cookies and mixed content.
+ mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+ mWebSettings.setAcceptThirdPartyCookies(true);
+ mWebSettings.getAwSettings().setZeroLayoutHeightDisablesViewportQuirk(true);
+ }
+
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ initForReal();
+ if (privateBrowsing) {
+ // Intentionally irreversibly disable the webview instance, so that private
+ // user data cannot leak through misuse of a non-privateBrowing WebView
+ // instance. Can't just null out mAwContents as we never null-check it
+ // before use.
+ destroy();
+ }
+ }
+ });
+ }
+
+ private void initForReal() {
+ mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView, mContext,
+ new InternalAccessAdapter(), new WebViewNativeGLDelegate(),
+ mContentsClientAdapter, mWebSettings.getAwSettings());
+
+ if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
+ // On KK and above, favicons are automatically downloaded as the method
+ // old apps use to enable that behavior is deprecated.
+ AwContents.setShouldDownloadFavicons();
+ }
+
+ AwContentsStatics.setRecordFullDocument(sRecordWholeDocumentEnabledByApi ||
+ mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP);
+
+ if (mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+ // Prior to Lollipop, JavaScript objects injected via addJavascriptInterface
+ // were not inspectable.
+ mAwContents.disableJavascriptInterfacesInspection();
+ }
+
+ // TODO: This assumes AwContents ignores second Paint param.
+ mAwContents.setLayerType(mWebView.getLayerType(), null);
+ }
+
+ void startYourEngine() {
+ mRunQueue.drainQueue();
+ }
+
+ private RuntimeException createThreadException() {
+ return new IllegalStateException(
+ "Calling View methods on another thread than the UI thread.");
+ }
+
+ private boolean checkNeedsPost() {
+ boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
+ if (!needsPost && mAwContents == null) {
+ throw new IllegalStateException(
+ "AwContents must be created if we are not posting!");
+ }
+ return needsPost;
+ }
+
+ // Intentionally not static, as no need to check thread on static methods
+ private void checkThread() {
+ if (!ThreadUtils.runningOnUiThread()) {
+ final RuntimeException threadViolation = createThreadException();
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ throw threadViolation;
+ }
+ });
+ throw createThreadException();
+ }
+ }
+
+ @Override
+ public void setHorizontalScrollbarOverlay(final boolean overlay) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setHorizontalScrollbarOverlay(overlay);
+ }
+ });
+ return;
+ }
+ mAwContents.setHorizontalScrollbarOverlay(overlay);
+ }
+
+ @Override
+ public void setVerticalScrollbarOverlay(final boolean overlay) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setVerticalScrollbarOverlay(overlay);
+ }
+ });
+ return;
+ }
+ mAwContents.setVerticalScrollbarOverlay(overlay);
+ }
+
+ @Override
+ public boolean overlayHorizontalScrollbar() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return overlayHorizontalScrollbar();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.overlayHorizontalScrollbar();
+ }
+
+ @Override
+ public boolean overlayVerticalScrollbar() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return overlayVerticalScrollbar();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.overlayVerticalScrollbar();
+ }
+
+ @Override
+ public int getVisibleTitleHeight() {
+ // This is deprecated in WebView and should always return 0.
+ return 0;
+ }
+
+ @Override
+ public SslCertificate getCertificate() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
+ @Override
+ public SslCertificate call() {
+ return getCertificate();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.getCertificate();
+ }
+
+ @Override
+ public void setCertificate(SslCertificate certificate) {
+ // intentional no-op
+ }
+
+ @Override
+ public void savePassword(String host, String username, String password) {
+ // This is a deprecated API: intentional no-op.
+ }
+
+ @Override
+ public void setHttpAuthUsernamePassword(final String host, final String realm,
+ final String username, final String password) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setHttpAuthUsernamePassword(host, realm, username, password);
+ }
+ });
+ return;
+ }
+ mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
+ }
+
+ @Override
+ public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
+ @Override
+ public String[] call() {
+ return getHttpAuthUsernamePassword(host, realm);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.getHttpAuthUsernamePassword(host, realm);
+ }
+
+ @Override
+ public void destroy() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ destroy();
+ }
+ });
+ return;
+ }
+
+ mAwContents.destroy();
+ if (mGLfunctor != null) {
+ mGLfunctor.destroy();
+ mGLfunctor = null;
+ }
+ }
+
+ @Override
+ public void setNetworkAvailable(final boolean networkUp) {
+ // Note that this purely toggles the JS navigator.online property.
+ // It does not in affect chromium or network stack state in any way.
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setNetworkAvailable(networkUp);
+ }
+ });
+ return;
+ }
+ mAwContents.setNetworkAvailable(networkUp);
+ }
+
+ @Override
+ public WebBackForwardList saveState(final Bundle outState) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
+ @Override
+ public WebBackForwardList call() {
+ return saveState(outState);
+ }
+ });
+ return ret;
+ }
+ if (outState == null) return null;
+ if (!mAwContents.saveState(outState)) return null;
+ return copyBackForwardList();
+ }
+
+ @Override
+ public boolean savePicture(Bundle b, File dest) {
+ // Intentional no-op: hidden method on WebView.
+ return false;
+ }
+
+ @Override
+ public boolean restorePicture(Bundle b, File src) {
+ // Intentional no-op: hidden method on WebView.
+ return false;
+ }
+
+ @Override
+ public WebBackForwardList restoreState(final Bundle inState) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
+ @Override
+ public WebBackForwardList call() {
+ return restoreState(inState);
+ }
+ });
+ return ret;
+ }
+ if (inState == null) return null;
+ if (!mAwContents.restoreState(inState)) return null;
+ return copyBackForwardList();
+ }
+
+ @Override
+ public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) {
+ // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
+
+ // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
+ // directly and any result of the evaluation will not replace the current page content.
+ // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
+ // have the result of that URL replace the content of the current page.
+ final String JAVASCRIPT_SCHEME = "javascript:";
+ if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
+ url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
+ url.substring(JAVASCRIPT_SCHEME.length()));
+ }
+ });
+ } else {
+ mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
+ url.substring(JAVASCRIPT_SCHEME.length()));
+ }
+ return;
+ }
+
+ LoadUrlParams params = new LoadUrlParams(url);
+ if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
+ loadUrlOnUiThread(params);
+ }
+
+ @Override
+ public void loadUrl(String url) {
+ // Early out to match old WebView implementation
+ if (url == null) {
+ return;
+ }
+ loadUrl(url, null);
+ }
+
+ @Override
+ public void postUrl(String url, byte[] postData) {
+ LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
+ Map<String,String> headers = new HashMap<String,String>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ params.setExtraHeaders(headers);
+ loadUrlOnUiThread(params);
+ }
+
+ private static String fixupMimeType(String mimeType) {
+ return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
+ }
+
+ private static String fixupData(String data) {
+ return TextUtils.isEmpty(data) ? "" : data;
+ }
+
+ private static String fixupBase(String url) {
+ return TextUtils.isEmpty(url) ? "about:blank" : url;
+ }
+
+ private static String fixupHistory(String url) {
+ return TextUtils.isEmpty(url) ? "about:blank" : url;
+ }
+
+ private static boolean isBase64Encoded(String encoding) {
+ return "base64".equals(encoding);
+ }
+
+ @Override
+ public void loadData(String data, String mimeType, String encoding) {
+ loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
+ fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
+ }
+
+ @Override
+ public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
+ String historyUrl) {
+ data = fixupData(data);
+ mimeType = fixupMimeType(mimeType);
+ LoadUrlParams loadUrlParams;
+ baseUrl = fixupBase(baseUrl);
+ historyUrl = fixupHistory(historyUrl);
+
+ if (baseUrl.startsWith("data:")) {
+ // For backwards compatibility with WebViewClassic, we use the value of |encoding|
+ // as the charset, as long as it's not "base64".
+ boolean isBase64 = isBase64Encoded(encoding);
+ loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
+ data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
+ } else {
+ // When loading data with a non-data: base URL, the classic WebView would effectively
+ // "dump" that string of data into the WebView without going through regular URL
+ // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
+ // base64 encoding the data that is passed here and then loading that as a data: URL.
+ try {
+ loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
+ Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
+ true, baseUrl, historyUrl, "utf-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.wtf(TAG, "Unable to load data string " + data, e);
+ return;
+ }
+ }
+ loadUrlOnUiThread(loadUrlParams);
+ }
+
+ private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
+ // This is the last point that we can delay starting the Chromium backend up
+ // and if the app has not caused us to bind the Chromium UI thread to a background thread
+ // we now bind Chromium's notion of the UI thread to the app main thread.
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ // Disallowed in WebView API for apps targetting a new SDK
+ assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ mAwContents.loadUrl(loadUrlParams);
+ }
+ });
+ return;
+ }
+ mAwContents.loadUrl(loadUrlParams);
+ }
+
+ public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
+ checkThread();
+ mAwContents.evaluateJavaScript(script, resultCallback);
+ }
+
+ @Override
+ public void saveWebArchive(String filename) {
+ saveWebArchive(filename, false, null);
+ }
+
+ @Override
+ public void saveWebArchive(final String basename, final boolean autoname,
+ final ValueCallback<String> callback) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ saveWebArchive(basename, autoname, callback);
+ }
+ });
+ return;
+ }
+ mAwContents.saveWebArchive(basename, autoname, callback);
+ }
+
+ @Override
+ public void stopLoading() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ stopLoading();
+ }
+ });
+ return;
+ }
+
+ mAwContents.stopLoading();
+ }
+
+ @Override
+ public void reload() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ reload();
+ }
+ });
+ return;
+ }
+ mAwContents.reload();
+ }
+
+ @Override
+ public boolean canGoBack() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return canGoBack();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.canGoBack();
+ }
+
+ @Override
+ public void goBack() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ goBack();
+ }
+ });
+ return;
+ }
+ mAwContents.goBack();
+ }
+
+ @Override
+ public boolean canGoForward() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return canGoForward();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.canGoForward();
+ }
+
+ @Override
+ public void goForward() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ goForward();
+ }
+ });
+ return;
+ }
+ mAwContents.goForward();
+ }
+
+ @Override
+ public boolean canGoBackOrForward(final int steps) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return canGoBackOrForward(steps);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.canGoBackOrForward(steps);
+ }
+
+ @Override
+ public void goBackOrForward(final int steps) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ goBackOrForward(steps);
+ }
+ });
+ return;
+ }
+ mAwContents.goBackOrForward(steps);
+ }
+
+ @Override
+ public boolean isPrivateBrowsingEnabled() {
+ // Not supported in this WebView implementation.
+ return false;
+ }
+
+ @Override
+ public boolean pageUp(final boolean top) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return pageUp(top);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.pageUp(top);
+ }
+
+ @Override
+ public boolean pageDown(final boolean bottom) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return pageDown(bottom);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.pageDown(bottom);
+ }
+
+ @Override
+ public void clearView() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearView();
+ }
+ });
+ return;
+ }
+ mAwContents.clearView();
+ }
+
+ @Override
+ public Picture capturePicture() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
+ @Override
+ public Picture call() {
+ return capturePicture();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.capturePicture();
+ }
+
+ @Override
+ public float getScale() {
+ // No checkThread() as it is mostly thread safe (workaround for b/10652991).
+ mFactory.startYourEngines(true);
+ return mAwContents.getScale();
+ }
+
+ @Override
+ public void setInitialScale(final int scaleInPercent) {
+ // No checkThread() as it is thread safe
+ mWebSettings.getAwSettings().setInitialPageScale(scaleInPercent);
+ }
+
+ @Override
+ public void invokeZoomPicker() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ invokeZoomPicker();
+ }
+ });
+ return;
+ }
+ mAwContents.invokeZoomPicker();
+ }
+
+ @Override
+ public WebView.HitTestResult getHitTestResult() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ WebView.HitTestResult ret = runOnUiThreadBlocking(
+ new Callable<WebView.HitTestResult>() {
+ @Override
+ public WebView.HitTestResult call() {
+ return getHitTestResult();
+ }
+ });
+ return ret;
+ }
+ AwContents.HitTestData data = mAwContents.getLastHitTestResult();
+ mHitTestResult.setType(data.hitTestResultType);
+ mHitTestResult.setExtra(data.hitTestResultExtraData);
+ return mHitTestResult;
+ }
+
+ @Override
+ public void requestFocusNodeHref(final Message hrefMsg) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ requestFocusNodeHref(hrefMsg);
+ }
+ });
+ return;
+ }
+ mAwContents.requestFocusNodeHref(hrefMsg);
+ }
+
+ @Override
+ public void requestImageRef(final Message msg) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ requestImageRef(msg);
+ }
+ });
+ return;
+ }
+ mAwContents.requestImageRef(msg);
+ }
+
+ @Override
+ public String getUrl() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ String ret = runOnUiThreadBlocking(new Callable<String>() {
+ @Override
+ public String call() {
+ return getUrl();
+ }
+ });
+ return ret;
+ }
+ String url = mAwContents.getUrl();
+ if (url == null || url.trim().isEmpty()) return null;
+ return url;
+ }
+
+ @Override
+ public String getOriginalUrl() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ String ret = runOnUiThreadBlocking(new Callable<String>() {
+ @Override
+ public String call() {
+ return getOriginalUrl();
+ }
+ });
+ return ret;
+ }
+ String url = mAwContents.getOriginalUrl();
+ if (url == null || url.trim().isEmpty()) return null;
+ return url;
+ }
+
+ @Override
+ public String getTitle() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ String ret = runOnUiThreadBlocking(new Callable<String>() {
+ @Override
+ public String call() {
+ return getTitle();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.getTitle();
+ }
+
+ @Override
+ public Bitmap getFavicon() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() {
+ @Override
+ public Bitmap call() {
+ return getFavicon();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.getFavicon();
+ }
+
+ @Override
+ public String getTouchIconUrl() {
+ // Intentional no-op: hidden method on WebView.
+ return null;
+ }
+
+ @Override
+ public int getProgress() {
+ if (mAwContents == null) return 100;
+ // No checkThread() because the value is cached java side (workaround for b/10533304).
+ return mAwContents.getMostRecentProgress();
+ }
+
+ @Override
+ public int getContentHeight() {
+ if (mAwContents == null) return 0;
+ // No checkThread() as it is mostly thread safe (workaround for b/10594869).
+ return mAwContents.getContentHeightCss();
+ }
+
+ @Override
+ public int getContentWidth() {
+ if (mAwContents == null) return 0;
+ // No checkThread() as it is mostly thread safe (workaround for b/10594869).
+ return mAwContents.getContentWidthCss();
+ }
+
+ @Override
+ public void pauseTimers() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ pauseTimers();
+ }
+ });
+ return;
+ }
+ mAwContents.pauseTimers();
+ }
+
+ @Override
+ public void resumeTimers() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ resumeTimers();
+ }
+ });
+ return;
+ }
+ mAwContents.resumeTimers();
+ }
+
+ @Override
+ public void onPause() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onPause();
+ }
+ });
+ return;
+ }
+ mAwContents.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onResume();
+ }
+ });
+ return;
+ }
+ mAwContents.onResume();
+ }
+
+ @Override
+ public boolean isPaused() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return isPaused();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.isPaused();
+ }
+
+ @Override
+ public void freeMemory() {
+ // Intentional no-op. Memory is managed automatically by Chromium.
+ }
+
+ @Override
+ public void clearCache(final boolean includeDiskFiles) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearCache(includeDiskFiles);
+ }
+ });
+ return;
+ }
+ mAwContents.clearCache(includeDiskFiles);
+ }
+
+ /**
+ * This is a poorly named method, but we keep it for historical reasons.
+ */
+ @Override
+ public void clearFormData() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearFormData();
+ }
+ });
+ return;
+ }
+ mAwContents.hideAutofillPopup();
+ }
+
+ @Override
+ public void clearHistory() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearHistory();
+ }
+ });
+ return;
+ }
+ mAwContents.clearHistory();
+ }
+
+ @Override
+ public void clearSslPreferences() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearSslPreferences();
+ }
+ });
+ return;
+ }
+ mAwContents.clearSslPreferences();
+ }
+
+ @Override
+ public WebBackForwardList copyBackForwardList() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
+ @Override
+ public WebBackForwardList call() {
+ return copyBackForwardList();
+ }
+ });
+ return ret;
+ }
+ return new WebBackForwardListChromium(
+ mAwContents.getNavigationHistory());
+ }
+
+ @Override
+ public void setFindListener(WebView.FindListener listener) {
+ mContentsClientAdapter.setFindListener(listener);
+ }
+
+ @Override
+ public void findNext(final boolean forwards) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ findNext(forwards);
+ }
+ });
+ return;
+ }
+ mAwContents.findNext(forwards);
+ }
+
+ @Override
+ public int findAll(final String searchString) {
+ findAllAsync(searchString);
+ return 0;
+ }
+
+ @Override
+ public void findAllAsync(final String searchString) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ findAllAsync(searchString);
+ }
+ });
+ return;
+ }
+ mAwContents.findAllAsync(searchString);
+ }
+
+ @Override
+ public boolean showFindDialog(final String text, final boolean showIme) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ return false;
+ }
+ if (mWebView.getParent() == null) {
+ return false;
+ }
+
+ FindActionModeCallback findAction = new FindActionModeCallback(mContext);
+ if (findAction == null) {
+ return false;
+ }
+
+ mWebView.startActionMode(findAction);
+ findAction.setWebView(mWebView);
+ if (showIme) {
+ findAction.showSoftInput();
+ }
+
+ if (text != null) {
+ findAction.setText(text);
+ findAction.findAll();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void notifyFindDialogDismissed() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ notifyFindDialogDismissed();
+ }
+ });
+ return;
+ }
+ clearMatches();
+ }
+
+ @Override
+ public void clearMatches() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ clearMatches();
+ }
+ });
+ return;
+ }
+ mAwContents.clearMatches();
+ }
+
+ @Override
+ public void documentHasImages(final Message response) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ documentHasImages(response);
+ }
+ });
+ return;
+ }
+ mAwContents.documentHasImages(response);
+ }
+
+ @Override
+ public void setWebViewClient(WebViewClient client) {
+ mContentsClientAdapter.setWebViewClient(client);
+ }
+
+ @Override
+ public void setDownloadListener(DownloadListener listener) {
+ mContentsClientAdapter.setDownloadListener(listener);
+ }
+
+ @Override
+ public void setWebChromeClient(WebChromeClient client) {
+ mWebSettings.getAwSettings().setFullscreenSupported(doesSupportFullscreen(client));
+ mContentsClientAdapter.setWebChromeClient(client);
+ }
+
+ /**
+ * Returns true if the supplied {@link WebChromeClient} supports fullscreen.
+ *
+ * <p>For fullscreen support, implementations of {@link WebChromeClient#onShowCustomView}
+ * and {@link WebChromeClient#onHideCustomView()} are required.
+ */
+ private boolean doesSupportFullscreen(WebChromeClient client) {
+ if (client == null) {
+ return false;
+ }
+ Class<?> clientClass = client.getClass();
+ boolean foundShowMethod = false;
+ boolean foundHideMethod = false;
+ while (clientClass != WebChromeClient.class && (!foundShowMethod || !foundHideMethod)) {
+ if (!foundShowMethod) {
+ try {
+ clientClass.getDeclaredMethod("onShowCustomView", View.class,
+ CustomViewCallback.class);
+ foundShowMethod = true;
+ } catch (NoSuchMethodException e) { }
+ }
+
+ if (!foundHideMethod) {
+ try {
+ clientClass.getDeclaredMethod("onHideCustomView");
+ foundHideMethod = true;
+ } catch (NoSuchMethodException e) { }
+ }
+ clientClass = clientClass.getSuperclass();
+ }
+ return foundShowMethod && foundHideMethod;
+ }
+
+ @Override
+ public void setPictureListener(final WebView.PictureListener listener) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setPictureListener(listener);
+ }
+ });
+ return;
+ }
+ mContentsClientAdapter.setPictureListener(listener);
+ mAwContents.enableOnNewPicture(listener != null,
+ mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
+ }
+
+ @Override
+ public void addJavascriptInterface(final Object obj, final String interfaceName) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ addJavascriptInterface(obj, interfaceName);
+ }
+ });
+ return;
+ }
+ Class<? extends Annotation> requiredAnnotation = null;
+ if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ requiredAnnotation = JavascriptInterface.class;
+ }
+ mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
+ }
+
+ @Override
+ public void removeJavascriptInterface(final String interfaceName) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ removeJavascriptInterface(interfaceName);
+ }
+ });
+ return;
+ }
+ mAwContents.removeJavascriptInterface(interfaceName);
+ }
+
+ @Override
+ public WebSettings getSettings() {
+ return mWebSettings;
+ }
+
+ @Override
+ public void setMapTrackballToArrowKeys(boolean setMap) {
+ // This is a deprecated API: intentional no-op.
+ }
+
+ @Override
+ public void flingScroll(final int vx, final int vy) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ flingScroll(vx, vy);
+ }
+ });
+ return;
+ }
+ mAwContents.flingScroll(vx, vy);
+ }
+
+ @Override
+ public View getZoomControls() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ return null;
+ }
+
+ // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
+ // to stop very out-dated applications from crashing.
+ Log.w(TAG, "WebView doesn't support getZoomControls");
+ return mAwContents.getSettings().supportZoom() ? new View(mContext) : null;
+ }
+
+ @Override
+ public boolean canZoomIn() {
+ if (checkNeedsPost()) {
+ return false;
+ }
+ return mAwContents.canZoomIn();
+ }
+
+ @Override
+ public boolean canZoomOut() {
+ if (checkNeedsPost()) {
+ return false;
+ }
+ return mAwContents.canZoomOut();
+ }
+
+ @Override
+ public boolean zoomIn() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return zoomIn();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.zoomIn();
+ }
+
+ @Override
+ public boolean zoomOut() {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return zoomOut();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.zoomOut();
+ }
+
+ @Override
+ public boolean zoomBy(float factor) {
+ mFactory.startYourEngines(true);
+ // This is an L API and therefore we can enforce stricter threading constraints.
+ checkThread();
+ return mAwContents.zoomBy(factor);
+ }
+
+ @Override
+ public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
+ // Intentional no-op
+ }
+
+ @Override
+ public View findHierarchyView(String className, int hashCode) {
+ // Intentional no-op
+ return null;
+ }
+
+ // WebViewProvider glue methods ---------------------------------------------------------------
+
+ @Override
+ // This needs to be kept thread safe!
+ public WebViewProvider.ViewDelegate getViewDelegate() {
+ return this;
+ }
+
+ @Override
+ // This needs to be kept thread safe!
+ public WebViewProvider.ScrollDelegate getScrollDelegate() {
+ return this;
+ }
+
+
+ // WebViewProvider.ViewDelegate implementation ------------------------------------------------
+
+ // TODO: remove from WebViewProvider and use default implementation from
+ // ViewGroup.
+ // @Override
+ public boolean shouldDelayChildPressedState() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return shouldDelayChildPressedState();
+ }
+ });
+ return ret;
+ }
+ return true;
+ }
+
+// @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ AccessibilityNodeProvider ret = runOnUiThreadBlocking(
+ new Callable<AccessibilityNodeProvider>() {
+ @Override
+ public AccessibilityNodeProvider call() {
+ return getAccessibilityNodeProvider();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.getAccessibilityNodeProvider();
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ runVoidTaskOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ onInitializeAccessibilityNodeInfo(info);
+ }
+ });
+ return;
+ }
+ mAwContents.onInitializeAccessibilityNodeInfo(info);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ runVoidTaskOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ onInitializeAccessibilityEvent(event);
+ }
+ });
+ return;
+ }
+ mAwContents.onInitializeAccessibilityEvent(event);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(final int action, final Bundle arguments) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return performAccessibilityAction(action, arguments);
+ }
+ });
+ return ret;
+ }
+ if (mAwContents.supportsAccessibilityAction(action)) {
+ return mAwContents.performAccessibilityAction(action, arguments);
+ }
+ return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
+ }
+
+ @Override
+ public void setOverScrollMode(final int mode) {
+ // This gets called from the android.view.View c'tor that WebView inherits from. This
+ // causes the method to be called when mAwContents == null.
+ // It's safe to ignore these calls however since AwContents will read the current value of
+ // this setting when it's created.
+ if (mAwContents == null) return;
+
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setOverScrollMode(mode);
+ }
+ });
+ return;
+ }
+ mAwContents.setOverScrollMode(mode);
+ }
+
+ @Override
+ public void setScrollBarStyle(final int style) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ setScrollBarStyle(style);
+ }
+ });
+ return;
+ }
+ mAwContents.setScrollBarStyle(style);
+ }
+
+ @Override
+ public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
+ final int t, final int r, final int b) {
+ // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
+ // WebViewChromium doesn't support that the vanilla implementation of this method can be
+ // used.
+ mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+ }
+
+ @Override
+ public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
+ final boolean clampedY) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ }
+ });
+ return;
+ }
+ mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ }
+
+ @Override
+ public void onWindowVisibilityChanged(final int visibility) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onWindowVisibilityChanged(visibility);
+ }
+ });
+ return;
+ }
+ mAwContents.onWindowVisibilityChanged(visibility);
+ }
+
+ @Override
+ public void onDraw(final Canvas canvas) {
+ mFactory.startYourEngines(true);
+ if (checkNeedsPost()) {
+ runVoidTaskOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ onDraw(canvas);
+ }
+ });
+ return;
+ }
+ mAwContents.onDraw(canvas);
+ }
+
+ @Override
+ public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
+ // This API is our strongest signal from the View system that this
+ // WebView is going to be bound to a View hierarchy and so at this
+ // point we must bind Chromium's UI thread to the current thread.
+ mFactory.startYourEngines(false);
+ checkThread();
+ mWebViewPrivate.super_setLayoutParams(layoutParams);
+ }
+
+ @Override
+ public boolean performLongClick() {
+ // Return false unless the WebView is attached to a View with a parent
+ return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false;
+ }
+
+ @Override
+ public void onConfigurationChanged(final Configuration newConfig) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onConfigurationChanged(newConfig);
+ }
+ });
+ return;
+ }
+ mAwContents.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ return null;
+ }
+ return mAwContents.onCreateInputConnection(outAttrs);
+ }
+
+ @Override
+ public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onKeyMultiple(keyCode, repeatCount, event);
+ }
+ });
+ return ret;
+ }
+ UnimplementedWebViewApi.invoke();
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(final int keyCode, final KeyEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onKeyDown(keyCode, event);
+ }
+ });
+ return ret;
+ }
+ UnimplementedWebViewApi.invoke();
+ return false;
+ }
+
+ @Override
+ public boolean onKeyUp(final int keyCode, final KeyEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onKeyUp(keyCode, event);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ // This API is our strongest signal from the View system that this
+ // WebView is going to be bound to a View hierarchy and so at this
+ // point we must bind Chromium's UI thread to the current thread.
+ mFactory.startYourEngines(false);
+ checkThread();
+ mAwContents.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onDetachedFromWindow();
+ }
+ });
+ return;
+ }
+
+ mAwContents.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onVisibilityChanged(final View changedView, final int visibility) {
+ // The AwContents will find out the container view visibility before the first draw so we
+ // can safely ignore onVisibilityChanged callbacks that happen before init().
+ if (mAwContents == null) return;
+
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onVisibilityChanged(changedView, visibility);
+ }
+ });
+ return;
+ }
+ mAwContents.onVisibilityChanged(changedView, visibility);
+ }
+
+ @Override
+ public void onWindowFocusChanged(final boolean hasWindowFocus) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onWindowFocusChanged(hasWindowFocus);
+ }
+ });
+ return;
+ }
+ mAwContents.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ @Override
+ public void onFocusChanged(final boolean focused, final int direction,
+ final Rect previouslyFocusedRect) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onFocusChanged(focused, direction, previouslyFocusedRect);
+ }
+ });
+ return;
+ }
+ mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public boolean setFrame(final int left, final int top, final int right, final int bottom) {
+ return mWebViewPrivate.super_setFrame(left, top, right, bottom);
+ }
+
+ @Override
+ public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onSizeChanged(w, h, ow, oh);
+ }
+ });
+ return;
+ }
+ mAwContents.onSizeChanged(w, h, ow, oh);
+ }
+
+ @Override
+ public void onScrollChanged(final int l, final int t, final int oldl, final int oldt) {
+ if (checkNeedsPost()) {
+ mRunQueue.addTask(new Runnable() {
+ @Override
+ public void run() {
+ onScrollChanged(l, t, oldl, oldt);
+ }
+ });
+ return;
+ }
+ mAwContents.onContainerViewScrollChanged(l, t, oldl, oldt);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(final KeyEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return dispatchKeyEvent(event);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent ev) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onTouchEvent(ev);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onHoverEvent(final MotionEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onHoverEvent(event);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.onHoverEvent(event);
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(final MotionEvent event) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return onGenericMotionEvent(event);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ // Trackball event not handled, which eventually gets converted to DPAD keyevents
+ return false;
+ }
+
+ @Override
+ public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return requestFocus(direction, previouslyFocusedRect);
+ }
+ });
+ return ret;
+ }
+ mAwContents.requestFocus();
+ return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ runVoidTaskOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ });
+ return;
+ }
+ mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
+ final boolean immediate) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return requestChildRectangleOnScreen(child, rect, immediate);
+ }
+ });
+ return ret;
+ }
+ return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
+ }
+
+ @Override
+ public void setBackgroundColor(final int color) {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ setBackgroundColor(color);
+ }
+ });
+ return;
+ }
+ mAwContents.setBackgroundColor(color);
+ }
+
+ @Override
+ public void setLayerType(final int layerType, final Paint paint) {
+ // This can be called from WebView constructor in which case mAwContents
+ // is still null. We set the layer type in initForReal in that case.
+ if (mAwContents == null) return;
+ if (checkNeedsPost()) {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ setLayerType(layerType, paint);
+ }
+ });
+ return;
+ }
+ mAwContents.setLayerType(layerType, paint);
+ }
+
+ // Remove from superclass
+ public void preDispatchDraw(Canvas canvas) {
+ // TODO(leandrogracia): remove this method from WebViewProvider if we think
+ // we won't need it again.
+ }
+
+ public void onStartTemporaryDetach() {
+ mAwContents.onStartTemporaryDetach();
+ }
+
+ public void onFinishTemporaryDetach() {
+ mAwContents.onFinishTemporaryDetach();
+ }
+
+ // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
+
+ @Override
+ public int computeHorizontalScrollRange() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ int ret = runOnUiThreadBlocking(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ return computeHorizontalScrollRange();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.computeHorizontalScrollRange();
+ }
+
+ @Override
+ public int computeHorizontalScrollOffset() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ int ret = runOnUiThreadBlocking(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ return computeHorizontalScrollOffset();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.computeHorizontalScrollOffset();
+ }
+
+ @Override
+ public int computeVerticalScrollRange() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ int ret = runOnUiThreadBlocking(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ return computeVerticalScrollRange();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.computeVerticalScrollRange();
+ }
+
+ @Override
+ public int computeVerticalScrollOffset() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ int ret = runOnUiThreadBlocking(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ return computeVerticalScrollOffset();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.computeVerticalScrollOffset();
+ }
+
+ @Override
+ public int computeVerticalScrollExtent() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ int ret = runOnUiThreadBlocking(new Callable<Integer>() {
+ @Override
+ public Integer call() {
+ return computeVerticalScrollExtent();
+ }
+ });
+ return ret;
+ }
+ return mAwContents.computeVerticalScrollExtent();
+ }
+
+ @Override
+ public void computeScroll() {
+ mFactory.startYourEngines(false);
+ if (checkNeedsPost()) {
+ runVoidTaskOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ computeScroll();
+ }
+ });
+ return;
+ }
+ mAwContents.computeScroll();
+ }
+
+ // TODO(sgurun) this is only to have master-gpl compiling.
+ public PrintDocumentAdapter createPrintDocumentAdapter() {
+ return createPrintDocumentAdapter("default");
+ }
+
+ //@Override TODO(sgurun) commenting this out to have master-gpl compiling.
+ public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
+ checkThread();
+ return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName);
+ }
+
+ // AwContents.NativeGLDelegate implementation --------------------------------------
+ private class WebViewNativeGLDelegate implements AwContents.NativeGLDelegate {
+ @Override
+ public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion,
+ View containerView) {
+ if (mGLfunctor == null) {
+ mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext(),
+ mFactory.getWebViewDelegate());
+ }
+ return mGLfunctor.requestDrawGL(canvas, containerView, waitForCompletion);
+ }
+
+ @Override
+ public void detachGLFunctor() {
+ if (mGLfunctor != null) {
+ mGLfunctor.detach();
+ }
+ }
+ }
+
+ // AwContents.InternalAccessDelegate implementation --------------------------------------
+ private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
+ @Override
+ public boolean drawChild(Canvas arg0, View arg1, long arg2) {
+ UnimplementedWebViewApi.invoke();
+ return false;
+ }
+
+ @Override
+ public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
+ // Intentional no-op
+ return false;
+ }
+
+ @Override
+ public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
+ UnimplementedWebViewApi.invoke();
+ return false;
+ }
+
+ @Override
+ public boolean super_dispatchKeyEvent(KeyEvent event) {
+ return mWebViewPrivate.super_dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean super_onGenericMotionEvent(MotionEvent arg0) {
+ return mWebViewPrivate.super_onGenericMotionEvent(arg0);
+ }
+
+ @Override
+ public void super_onConfigurationChanged(Configuration arg0) {
+ // Intentional no-op
+ }
+
+ @Override
+ public int super_getScrollBarStyle() {
+ return mWebViewPrivate.super_getScrollBarStyle();
+ }
+
+ @Override
+ public boolean awakenScrollBars() {
+ mWebViewPrivate.awakenScrollBars(0);
+ // TODO: modify the WebView.PrivateAccess to provide a return value.
+ return true;
+ }
+
+ @Override
+ public boolean super_awakenScrollBars(int arg0, boolean arg1) {
+ // TODO: need method on WebView.PrivateAccess?
+ UnimplementedWebViewApi.invoke();
+ return false;
+ }
+
+ @Override
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ // Intentional no-op.
+ // Chromium calls this directly to trigger accessibility events. That isn't needed
+ // for WebView since super_scrollTo invokes onScrollChanged for us.
+ }
+
+ @Override
+ public void overScrollBy(int deltaX, int deltaY,
+ int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY,
+ int maxOverScrollX, int maxOverScrollY,
+ boolean isTouchEvent) {
+ mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
+ scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
+ }
+
+ @Override
+ public void super_scrollTo(int scrollX, int scrollY) {
+ mWebViewPrivate.super_scrollTo(scrollX, scrollY);
+ }
+
+ @Override
+ public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+ mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ // @Override
+ public boolean super_onHoverEvent(MotionEvent event) {
+ return mWebViewPrivate.super_onHoverEvent(event);
+ }
+ }
+
+ // Implements SmartClipProvider
+ @Override
+ public void extractSmartClipData(int x, int y, int width, int height) {
+ checkThread();
+ mAwContents.extractSmartClipData(x, y, width, height);
+ }
+
+ // Implements SmartClipProvider
+ @Override
+ public void setSmartClipResultHandler(final Handler resultHandler) {
+ checkThread();
+ mAwContents.setSmartClipResultHandler(resultHandler);
+ }
+
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
new file mode 100644
index 0000000..60c7966
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -0,0 +1,469 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Looper;
+import android.os.StrictMode;
+import android.util.Log;
+import android.webkit.CookieManager;
+import android.webkit.GeolocationPermissions;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
+import android.webkit.WebView;
+import android.webkit.WebViewDatabase;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
+import android.webkit.WebViewProvider;
+
+import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate;
+
+import org.chromium.android_webview.AwBrowserContext;
+import org.chromium.android_webview.AwBrowserProcess;
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsStatics;
+import org.chromium.android_webview.AwCookieManager;
+import org.chromium.android_webview.AwDevToolsServer;
+import org.chromium.android_webview.AwFormDatabase;
+import org.chromium.android_webview.AwGeolocationPermissions;
+import org.chromium.android_webview.AwQuotaManagerBridge;
+import org.chromium.android_webview.AwResource;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.base.CommandLine;
+import org.chromium.base.MemoryPressureListener;
+import org.chromium.base.PathService;
+import org.chromium.base.PathUtils;
+import org.chromium.base.ResourceExtractor;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.content.app.ContentMain;
+import org.chromium.content.browser.ContentViewStatics;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
+
+ private static final String TAG = "WebViewChromiumFactoryProvider";
+
+ private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs";
+ private static final String VERSION_CODE_PREF = "lastVersionCodeUsed";
+ private static final String COMMAND_LINE_FILE = "/data/local/tmp/webview-command-line";
+
+ // Guards accees to the other members, and is notifyAll() signalled on the UI thread
+ // when the chromium process has been started.
+ private final Object mLock = new Object();
+
+ // Initialization guarded by mLock.
+ private AwBrowserContext mBrowserContext;
+ private Statics mStaticMethods;
+ private GeolocationPermissionsAdapter mGeolocationPermissions;
+ private CookieManagerAdapter mCookieManager;
+ private WebIconDatabaseAdapter mWebIconDatabase;
+ private WebStorageAdapter mWebStorage;
+ private WebViewDatabaseAdapter mWebViewDatabase;
+ private AwDevToolsServer mDevToolsServer;
+
+ private ArrayList<WeakReference<WebViewChromium>> mWebViewsToStart =
+ new ArrayList<WeakReference<WebViewChromium>>();
+
+ // Read/write protected by mLock.
+ private boolean mStarted;
+
+ private SharedPreferences mWebViewPrefs;
+ private WebViewDelegate mWebViewDelegate;
+
+ /**
+ * Constructor called by the API 21 version of {@link WebViewFactory} and earlier.
+ */
+ public WebViewChromiumFactoryProvider() {
+ initialize(WebViewDelegateFactory.createApi21CompatibilityDelegate());
+ }
+
+ /**
+ * Constructor called by the API 22 version of {@link WebViewFactory} and later.
+ */
+ public WebViewChromiumFactoryProvider(android.webkit.WebViewDelegate delegate) {
+ initialize(WebViewDelegateFactory.createProxyDelegate(delegate));
+ }
+
+ private void initialize(WebViewDelegate webViewDelegate) {
+ mWebViewDelegate = webViewDelegate;
+ if (isBuildDebuggable()) {
+ // Suppress the StrictMode violation as this codepath is only hit on debugglable builds.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ CommandLine.initFromFile(COMMAND_LINE_FILE);
+ StrictMode.setThreadPolicy(oldPolicy);
+ } else {
+ CommandLine.init(null);
+ }
+
+ CommandLine cl = CommandLine.getInstance();
+ // TODO: currently in a relase build the DCHECKs only log. We either need to insall
+ // a report handler with SetLogReportHandler to make them assert, or else compile
+ // them out of the build altogether (b/8284203). Either way, so long they're
+ // compiled in, we may as unconditionally enable them here.
+ cl.appendSwitch("enable-dcheck");
+
+ ThreadUtils.setWillOverrideUiThread();
+ // Load chromium library.
+ AwBrowserProcess.loadLibrary();
+
+ final PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo();
+
+ // Register the handler that will append the WebView version to logcat in case of a crash.
+ AwContentsStatics.registerCrashHandler(
+ "Version " + packageInfo.versionName + " (code " + packageInfo.versionCode + ")");
+
+ // Load glue-layer support library.
+ System.loadLibrary("webviewchromium_plat_support");
+
+ // Use shared preference to check for package downgrade.
+ mWebViewPrefs = mWebViewDelegate.getApplication().getSharedPreferences(
+ CHROMIUM_PREFS_NAME, Context.MODE_PRIVATE);
+ int lastVersion = mWebViewPrefs.getInt(VERSION_CODE_PREF, 0);
+ int currentVersion = packageInfo.versionCode;
+ if (lastVersion > currentVersion) {
+ // The WebView package has been downgraded since we last ran in this application.
+ // Delete the WebView data directory's contents.
+ String dataDir = PathUtils.getDataDirectory(mWebViewDelegate.getApplication());
+ Log.i(TAG, "WebView package downgraded from " + lastVersion + " to " + currentVersion +
+ "; deleting contents of " + dataDir);
+ deleteContents(new File(dataDir));
+ }
+ if (lastVersion != currentVersion) {
+ mWebViewPrefs.edit().putInt(VERSION_CODE_PREF, currentVersion).apply();
+ }
+ // Now safe to use WebView data directory.
+ }
+
+ private static boolean isBuildDebuggable() {
+ return !Build.TYPE.equals("user");
+ }
+
+ private static void deleteContents(File dir) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ if (!file.delete()) {
+ Log.w(TAG, "Failed to delete " + file);
+ }
+ }
+ }
+ }
+
+ private void initPlatSupportLibrary() {
+ DrawGLFunctor.setChromiumAwDrawGLFunction(AwContents.getAwDrawGLFunction());
+ AwContents.setAwDrawSWFunctionTable(GraphicsUtils.getDrawSWFunctionTable());
+ AwContents.setAwDrawGLFunctionTable(GraphicsUtils.getDrawGLFunctionTable());
+ }
+
+ private void ensureChromiumStartedLocked(boolean onMainThread) {
+ assert Thread.holdsLock(mLock);
+
+ if (mStarted) { // Early-out for the common case.
+ return;
+ }
+
+ Looper looper = !onMainThread ? Looper.myLooper() : Looper.getMainLooper();
+ Log.v(TAG, "Binding Chromium to " +
+ (Looper.getMainLooper().equals(looper) ? "main":"background") +
+ " looper " + looper);
+ ThreadUtils.setUiThread(looper);
+
+ if (ThreadUtils.runningOnUiThread()) {
+ startChromiumLocked();
+ return;
+ }
+
+ // We must post to the UI thread to cover the case that the user has invoked Chromium
+ // startup by using the (thread-safe) CookieManager rather than creating a WebView.
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ startChromiumLocked();
+ }
+ }
+ });
+ while (!mStarted) {
+ try {
+ // Important: wait() releases |mLock| the UI thread can take it :-)
+ mLock.wait();
+ } catch (InterruptedException e) {
+ // Keep trying... eventually the UI thread will process the task we sent it.
+ }
+ }
+ }
+
+ private void startChromiumLocked() {
+ assert Thread.holdsLock(mLock) && ThreadUtils.runningOnUiThread();
+
+ // The post-condition of this method is everything is ready, so notify now to cover all
+ // return paths. (Other threads will not wake-up until we release |mLock|, whatever).
+ mLock.notifyAll();
+
+ if (mStarted) {
+ return;
+ }
+
+ // We don't need to extract any paks because for WebView, they are
+ // in the system image.
+ ResourceExtractor.setMandatoryPaksToExtract("");
+
+ try {
+ LibraryLoader.ensureInitialized();
+ } catch(ProcessInitException e) {
+ throw new RuntimeException("Error initializing WebView library", e);
+ }
+
+ PathService.override(PathService.DIR_MODULE, "/system/lib/");
+ // TODO: DIR_RESOURCE_PAKS_ANDROID needs to live somewhere sensible,
+ // inlined here for simplicity setting up the HTMLViewer demo. Unfortunately
+ // it can't go into base.PathService, as the native constant it refers to
+ // lives in the ui/ layer. See ui/base/ui_base_paths.h
+ final int DIR_RESOURCE_PAKS_ANDROID = 3003;
+ PathService.override(DIR_RESOURCE_PAKS_ANDROID,
+ "/system/framework/webview/paks");
+
+ // Make sure that ResourceProvider is initialized before starting the browser process.
+ Context context = getWrappedCurrentApplicationContext();
+ setUpResources(context);
+ initPlatSupportLibrary();
+ AwBrowserProcess.start(context);
+
+ if (isBuildDebuggable()) {
+ setWebContentsDebuggingEnabled(true);
+ }
+
+ TraceEvent.setATraceEnabled(mWebViewDelegate.isTraceTagEnabled());
+ mWebViewDelegate.setOnTraceEnabledChangeListener(
+ new WebViewDelegate.OnTraceEnabledChangeListener() {
+ @Override
+ public void onTraceEnabledChange(boolean enabled) {
+ TraceEvent.setATraceEnabled(enabled);
+ }
+ });
+ mStarted = true;
+
+ for (WeakReference<WebViewChromium> wvc : mWebViewsToStart) {
+ WebViewChromium w = wvc.get();
+ if (w != null) {
+ w.startYourEngine();
+ }
+ }
+ mWebViewsToStart.clear();
+ mWebViewsToStart = null;
+ }
+
+ boolean hasStarted() {
+ return mStarted;
+ }
+
+ void startYourEngines(boolean onMainThread) {
+ synchronized (mLock) {
+ ensureChromiumStartedLocked(onMainThread);
+
+ }
+ }
+
+ private Context getWrappedCurrentApplicationContext() {
+ return ResourcesContextWrapperFactory.get(mWebViewDelegate.getApplication());
+ }
+
+ AwBrowserContext getBrowserContext() {
+ synchronized (mLock) {
+ return getBrowserContextLocked();
+ }
+ }
+
+ private AwBrowserContext getBrowserContextLocked() {
+ assert Thread.holdsLock(mLock);
+ assert mStarted;
+ if (mBrowserContext == null) {
+ mBrowserContext = new AwBrowserContext(mWebViewPrefs);
+ }
+ return mBrowserContext;
+ }
+
+ private void setWebContentsDebuggingEnabled(boolean enable) {
+ if (Looper.myLooper() != ThreadUtils.getUiThreadLooper()) {
+ throw new RuntimeException(
+ "Toggling of Web Contents Debugging must be done on the UI thread");
+ }
+ if (mDevToolsServer == null) {
+ if (!enable) return;
+ mDevToolsServer = new AwDevToolsServer();
+ }
+ mDevToolsServer.setRemoteDebuggingEnabled(enable);
+ }
+
+ private void setUpResources(Context context) {
+ // The resources are always called com.android.webview even if the manifest has had the
+ // package renamed.
+ ResourceRewriter.rewriteRValues(
+ mWebViewDelegate.getPackageId(context.getResources(), "com.android.webview"));
+
+ AwResource.setResources(context.getResources());
+ AwResource.setErrorPageResources(android.R.raw.loaderror,
+ android.R.raw.nodomain);
+ AwResource.setConfigKeySystemUuidMapping(
+ android.R.array.config_keySystemUuidMapping);
+ }
+
+ @Override
+ public Statics getStatics() {
+ synchronized (mLock) {
+ if (mStaticMethods == null) {
+ // TODO: Optimization potential: most these methods only need the native library
+ // loaded and initialized, not the entire browser process started.
+ // See also http://b/7009882
+ ensureChromiumStartedLocked(true);
+ mStaticMethods = new WebViewFactoryProvider.Statics() {
+ @Override
+ public String findAddress(String addr) {
+ return ContentViewStatics.findAddress(addr);
+ }
+
+ @Override
+ public String getDefaultUserAgent(Context context) {
+ return AwSettings.getDefaultUserAgent();
+ }
+
+ @Override
+ public void setWebContentsDebuggingEnabled(boolean enable) {
+ // Web Contents debugging is always enabled on debug builds.
+ if (!isBuildDebuggable()) {
+ WebViewChromiumFactoryProvider.this.
+ setWebContentsDebuggingEnabled(enable);
+ }
+ }
+
+ // TODO enable after L release to AOSP
+ //@Override
+ public void clearClientCertPreferences(Runnable onCleared) {
+ AwContentsStatics.clearClientCertPreferences(onCleared);
+ }
+
+ @Override
+ public void freeMemoryForTests() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ MemoryPressureListener.maybeNotifyMemoryPresure(
+ ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+ }
+ }
+
+ // TODO: Add @Override.
+ public void enableSlowWholeDocumentDraw() {
+ WebViewChromium.enableSlowWholeDocumentDraw();
+ }
+
+ @Override
+ public Uri[] parseFileChooserResult(int resultCode, Intent intent) {
+ return FileChooserParamsAdapter.parseFileChooserResult(resultCode, intent);
+ }
+ };
+ }
+ }
+ return mStaticMethods;
+ }
+
+ @Override
+ public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
+ WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);
+
+ synchronized (mLock) {
+ if (mWebViewsToStart != null) {
+ mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc));
+ }
+ }
+
+ return wvc;
+ }
+
+ @Override
+ public GeolocationPermissions getGeolocationPermissions() {
+ synchronized (mLock) {
+ if (mGeolocationPermissions == null) {
+ ensureChromiumStartedLocked(true);
+ mGeolocationPermissions = new GeolocationPermissionsAdapter(
+ getBrowserContextLocked().getGeolocationPermissions());
+ }
+ }
+ return mGeolocationPermissions;
+ }
+
+ @Override
+ public CookieManager getCookieManager() {
+ synchronized (mLock) {
+ if (mCookieManager == null) {
+ if (!mStarted) {
+ // We can use CookieManager without starting Chromium; the native code
+ // will bring up just the parts it needs to make this work on a temporary
+ // basis until Chromium is started for real. The temporary cookie manager
+ // needs the application context to have been set.
+ ContentMain.initApplicationContext(getWrappedCurrentApplicationContext());
+ }
+ mCookieManager = new CookieManagerAdapter(new AwCookieManager());
+ }
+ }
+ return mCookieManager;
+ }
+
+ @Override
+ public WebIconDatabase getWebIconDatabase() {
+ synchronized (mLock) {
+ if (mWebIconDatabase == null) {
+ ensureChromiumStartedLocked(true);
+ mWebIconDatabase = new WebIconDatabaseAdapter();
+ }
+ }
+ return mWebIconDatabase;
+ }
+
+ @Override
+ public WebStorage getWebStorage() {
+ synchronized (mLock) {
+ if (mWebStorage == null) {
+ ensureChromiumStartedLocked(true);
+ mWebStorage = new WebStorageAdapter(AwQuotaManagerBridge.getInstance());
+ }
+ }
+ return mWebStorage;
+ }
+
+ @Override
+ public WebViewDatabase getWebViewDatabase(Context context) {
+ synchronized (mLock) {
+ if (mWebViewDatabase == null) {
+ ensureChromiumStartedLocked(true);
+ AwBrowserContext browserContext = getBrowserContextLocked();
+ mWebViewDatabase = new WebViewDatabaseAdapter(
+ browserContext.getFormDatabase(),
+ browserContext.getHttpAuthDatabase(context));
+ }
+ }
+ return mWebViewDatabase;
+ }
+
+ WebViewDelegate getWebViewDelegate() {
+ return mWebViewDelegate;
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
new file mode 100644
index 0000000..c6b0c82
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -0,0 +1,1118 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
+import android.net.http.SslError;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Browser;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.webkit.ClientCertRequest;
+import android.webkit.ConsoleMessage;
+import android.webkit.DownloadListener;
+import android.webkit.GeolocationPermissions;
+import android.webkit.JsDialogHelper;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.PermissionRequest;
+import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient;
+import android.webkit.WebChromeClient.CustomViewCallback;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate;
+
+import org.chromium.android_webview.AwContentsClient;
+import org.chromium.android_webview.AwContentsClientBridge;
+import org.chromium.android_webview.AwHttpAuthHandler;
+import org.chromium.android_webview.AwWebResourceResponse;
+import org.chromium.android_webview.JsPromptResultReceiver;
+import org.chromium.android_webview.JsResultReceiver;
+import org.chromium.android_webview.permission.AwPermissionRequest;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.TraceEvent;
+import org.chromium.content.browser.ContentView;
+import org.chromium.content.browser.ContentViewClient;
+
+import java.lang.ref.WeakReference;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * An adapter class that forwards the callbacks from {@link ContentViewClient}
+ * to the appropriate {@link WebViewClient} or {@link WebChromeClient}.
+ *
+ * An instance of this class is associated with one {@link WebViewChromium}
+ * instance. A WebViewChromium is a WebView implementation provider (that is
+ * android.webkit.WebView delegates all functionality to it) and has exactly
+ * one corresponding {@link ContentView} instance.
+ *
+ * A {@link ContentViewClient} may be shared between multiple {@link ContentView}s,
+ * and hence multiple WebViews. Many WebViewClient methods pass the source
+ * WebView as an argument. This means that we either need to pass the
+ * corresponding ContentView to the corresponding ContentViewClient methods,
+ * or use an instance of ContentViewClientAdapter per WebViewChromium, to
+ * allow the source WebView to be injected by ContentViewClientAdapter. We
+ * choose the latter, because it makes for a cleaner design.
+ */
+public class WebViewContentsClientAdapter extends AwContentsClient {
+ // TAG is chosen for consistency with classic webview tracing.
+ private static final String TAG = "WebViewCallback";
+ // Enables API callback tracing
+ private static final boolean TRACE = false;
+ // The WebView instance that this adapter is serving.
+ private final WebView mWebView;
+ // The Context to use. This is different from mWebView.getContext(), which should not be used.
+ private final Context mContext;
+ // The WebViewClient instance that was passed to WebView.setWebViewClient().
+ private WebViewClient mWebViewClient;
+ // The WebChromeClient instance that was passed to WebView.setContentViewClient().
+ private WebChromeClient mWebChromeClient;
+ // The listener receiving find-in-page API results.
+ private WebView.FindListener mFindListener;
+ // The listener receiving notifications of screen updates.
+ private WebView.PictureListener mPictureListener;
+
+ private WebViewDelegate mWebViewDelegate;
+
+ private DownloadListener mDownloadListener;
+
+ private Handler mUiThreadHandler;
+
+ private static final int NEW_WEBVIEW_CREATED = 100;
+
+ private WeakHashMap<AwPermissionRequest, WeakReference<PermissionRequestAdapter>>
+ mOngoingPermissionRequests;
+ /**
+ * Adapter constructor.
+ *
+ * @param webView the {@link WebView} instance that this adapter is serving.
+ */
+ WebViewContentsClientAdapter(WebView webView, Context context,
+ WebViewDelegate webViewDelegate) {
+ if (webView == null || webViewDelegate == null) {
+ throw new IllegalArgumentException("webView or delegate can't be null.");
+ }
+
+ if (context == null) {
+ throw new IllegalArgumentException("context can't be null.");
+ }
+
+ mContext = context;
+ mWebView = webView;
+ mWebViewDelegate = webViewDelegate;
+ setWebViewClient(null);
+
+ mUiThreadHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case NEW_WEBVIEW_CREATED:
+ WebView.WebViewTransport t = (WebView.WebViewTransport) msg.obj;
+ WebView newWebView = t.getWebView();
+ if (newWebView == mWebView) {
+ throw new IllegalArgumentException(
+ "Parent WebView cannot host it's own popup window. Please " +
+ "use WebSettings.setSupportMultipleWindows(false)");
+ }
+
+ if (newWebView != null && newWebView.copyBackForwardList().getSize() != 0) {
+ throw new IllegalArgumentException(
+ "New WebView for popup window must not have been previously " +
+ "navigated.");
+ }
+
+ WebViewChromium.completeWindowCreation(mWebView, newWebView);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ };
+
+ }
+
+ // WebViewClassic is coded in such a way that even if a null WebViewClient is set,
+ // certain actions take place.
+ // We choose to replicate this behavior by using a NullWebViewClient implementation (also known
+ // as the Null Object pattern) rather than duplicating the WebViewClassic approach in
+ // ContentView.
+ static class NullWebViewClient extends WebViewClient {
+ @Override
+ public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
+ // TODO: Investigate more and add a test case.
+ // This is reflecting Clank's behavior.
+ int keyCode = event.getKeyCode();
+ return !ContentViewClient.shouldPropagateKey(keyCode);
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ Intent intent;
+ // Perform generic parsing of the URI to turn it into an Intent.
+ try {
+ intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException ex) {
+ Log.w(TAG, "Bad URI " + url + ": " + ex.getMessage());
+ return false;
+ }
+ // Sanitize the Intent, ensuring web pages can not bypass browser
+ // security (only access to BROWSABLE activities).
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ intent.setComponent(null);
+ Intent selector = intent.getSelector();
+ if (selector != null) {
+ selector.addCategory(Intent.CATEGORY_BROWSABLE);
+ selector.setComponent(null);
+ }
+ // Pass the package name as application ID so that the intent from the
+ // same application can be opened in the same tab.
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID,
+ view.getContext().getPackageName());
+
+ Context context = view.getContext();
+ if (!(context instanceof Activity)) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException ex) {
+ Log.w(TAG, "No application can handle " + url);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ void setWebViewClient(WebViewClient client) {
+ if (client != null) {
+ mWebViewClient = client;
+ } else {
+ mWebViewClient = new NullWebViewClient();
+ }
+ }
+
+ void setWebChromeClient(WebChromeClient client) {
+ mWebChromeClient = client;
+ }
+
+ void setDownloadListener(DownloadListener listener) {
+ mDownloadListener = listener;
+ }
+
+ void setFindListener(WebView.FindListener listener) {
+ mFindListener = listener;
+ }
+
+ void setPictureListener(WebView.PictureListener listener) {
+ mPictureListener = listener;
+ }
+
+ //--------------------------------------------------------------------------------------------
+ // Adapter for all the methods.
+ //--------------------------------------------------------------------------------------------
+
+ /**
+ * @see AwContentsClient#getVisitedHistory
+ */
+ @Override
+ public void getVisitedHistory(ValueCallback<String[]> callback) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "getVisitedHistory");
+ mWebChromeClient.getVisitedHistory(callback);
+ }
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#doUpdateVisiteHistory(String, boolean)
+ */
+ @Override
+ public void doUpdateVisitedHistory(String url, boolean isReload) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "doUpdateVisitedHistory=" + url + " reload=" + isReload);
+ mWebViewClient.doUpdateVisitedHistory(mWebView, url, isReload);
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#onProgressChanged(int)
+ */
+ @Override
+ public void onProgressChanged(int progress) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onProgressChanged=" + progress);
+ mWebChromeClient.onProgressChanged(mWebView, progress);
+ }
+ TraceEvent.end();
+ }
+
+ private static class WebResourceRequestImpl implements WebResourceRequest {
+ private final ShouldInterceptRequestParams mParams;
+
+ public WebResourceRequestImpl(ShouldInterceptRequestParams params) {
+ mParams = params;
+ }
+
+ @Override
+ public Uri getUrl() {
+ return Uri.parse(mParams.url);
+ }
+
+ @Override
+ public boolean isForMainFrame() {
+ return mParams.isMainFrame;
+ }
+
+ @Override
+ public boolean hasGesture() {
+ return mParams.hasUserGesture;
+ }
+
+ @Override
+ public String getMethod() {
+ return mParams.method;
+ }
+
+ @Override
+ public Map<String, String> getRequestHeaders() {
+ return mParams.requestHeaders;
+ }
+ }
+
+ /**
+ * @see AwContentsClient#shouldInterceptRequest(java.lang.String)
+ */
+ @Override
+ public AwWebResourceResponse shouldInterceptRequest(ShouldInterceptRequestParams params) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "shouldInterceptRequest=" + params.url);
+ WebResourceResponse response = mWebViewClient.shouldInterceptRequest(mWebView,
+ new WebResourceRequestImpl(params));
+ TraceEvent.end();
+ if (response == null) return null;
+
+ // AwWebResourceResponse should support null headers. b/16332774.
+ Map<String, String> responseHeaders = response.getResponseHeaders();
+ if (responseHeaders == null)
+ responseHeaders = new HashMap<String, String>();
+
+ return new AwWebResourceResponse(
+ response.getMimeType(),
+ response.getEncoding(),
+ response.getData(),
+ response.getStatusCode(),
+ response.getReasonPhrase(),
+ responseHeaders);
+ }
+
+ /**
+ * @see AwContentsClient#shouldOverrideUrlLoading(java.lang.String)
+ */
+ @Override
+ public boolean shouldOverrideUrlLoading(String url) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + url);
+ boolean result = mWebViewClient.shouldOverrideUrlLoading(mWebView, url);
+ TraceEvent.end();
+ return result;
+ }
+
+ /**
+ * @see AwContentsClient#onUnhandledKeyEvent(android.view.KeyEvent)
+ */
+ @Override
+ public void onUnhandledKeyEvent(KeyEvent event) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onUnhandledKeyEvent");
+ mWebViewClient.onUnhandledKeyEvent(mWebView, event);
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#onConsoleMessage(android.webkit.ConsoleMessage)
+ */
+ @Override
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ TraceEvent.begin();
+ boolean result;
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onConsoleMessage: " + consoleMessage.message());
+ result = mWebChromeClient.onConsoleMessage(consoleMessage);
+ String message = consoleMessage.message();
+ if (result && message != null && message.startsWith("[blocked]")) {
+ Log.e(TAG, "Blocked URL: " + message);
+ }
+ } else {
+ result = false;
+ }
+ TraceEvent.end();
+ return result;
+ }
+
+ /**
+ * @see AwContentsClient#onFindResultReceived(int,int,boolean)
+ */
+ @Override
+ public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
+ boolean isDoneCounting) {
+ if (mFindListener == null) return;
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onFindResultReceived");
+ mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
+ TraceEvent.end();
+ }
+
+ /**
+ * @See AwContentsClient#onNewPicture(Picture)
+ */
+ @Override
+ public void onNewPicture(Picture picture) {
+ if (mPictureListener == null) return;
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onNewPicture");
+ mPictureListener.onNewPicture(mWebView, picture);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onLoadResource(String url) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onLoadResource=" + url);
+ mWebViewClient.onLoadResource(mWebView, url);
+ TraceEvent.end();
+ }
+
+ @Override
+ public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
+ Message m = mUiThreadHandler.obtainMessage(
+ NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport());
+ TraceEvent.begin();
+ boolean result;
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onCreateWindow");
+ result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m);
+ } else {
+ result = false;
+ }
+ TraceEvent.end();
+ return result;
+ }
+
+ /**
+ * @see AwContentsClient#onCloseWindow()
+ */
+ @Override
+ public void onCloseWindow() {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onCloseWindow");
+ mWebChromeClient.onCloseWindow(mWebView);
+ }
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#onRequestFocus()
+ */
+ @Override
+ public void onRequestFocus() {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onRequestFocus");
+ mWebChromeClient.onRequestFocus(mWebView);
+ }
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed)
+ */
+ @Override
+ public void onReceivedTouchIconUrl(String url, boolean precomposed) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onReceivedTouchIconUrl=" + url);
+ mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed);
+ }
+ TraceEvent.end();
+ }
+
+ /**
+ * @see AwContentsClient#onReceivedIcon(Bitmap bitmap)
+ */
+ @Override
+ public void onReceivedIcon(Bitmap bitmap) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onReceivedIcon");
+ mWebChromeClient.onReceivedIcon(mWebView, bitmap);
+ }
+ TraceEvent.end();
+ }
+
+ /**
+ * @see ContentViewClient#onPageStarted(String)
+ */
+ @Override
+ public void onPageStarted(String url) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onPageStarted=" + url);
+ mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon());
+ TraceEvent.end();
+ }
+
+ /**
+ * @see ContentViewClient#onPageFinished(String)
+ */
+ @Override
+ public void onPageFinished(String url) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onPageFinished=" + url);
+ mWebViewClient.onPageFinished(mWebView, url);
+ TraceEvent.end();
+
+ // See b/8208948
+ // This fakes an onNewPicture callback after onPageFinished to allow
+ // CTS tests to run in an un-flaky manner. This is required as the
+ // path for sending Picture updates in Chromium are decoupled from the
+ // page loading callbacks, i.e. the Chrome compositor may draw our
+ // content and send the Picture before onPageStarted or onPageFinished
+ // are invoked. The CTS harness discards any pictures it receives before
+ // onPageStarted is invoked, so in the case we get the Picture before that and
+ // no further updates after onPageStarted, we'll fail the test by timing
+ // out waiting for a Picture.
+ // To ensure backwards compatibility, we need to defer sending Picture updates
+ // until onPageFinished has been invoked. This work is being done
+ // upstream, and we can revert this hack when it lands.
+ if (mPictureListener != null) {
+ ThreadUtils.postOnUiThreadDelayed(new Runnable() {
+ @Override
+ public void run() {
+ UnimplementedWebViewApi.invoke();
+ if (mPictureListener != null) {
+ if (TRACE) Log.d(TAG, "onPageFinished-fake");
+ mPictureListener.onNewPicture(mWebView, new Picture());
+ }
+ }
+ }, 100);
+ }
+ }
+
+ /**
+ * @see ContentViewClient#onReceivedError(int,String,String)
+ */
+ @Override
+ public void onReceivedError(int errorCode, String description, String failingUrl) {
+ if (description == null || description.isEmpty()) {
+ // ErrorStrings is @hidden, so we can't do this in AwContents.
+ // Normally the net/ layer will set a valid description, but for synthesized callbacks
+ // (like in the case for intercepted requests) AwContents will pass in null.
+ description = mWebViewDelegate.getErrorString(mContext, errorCode);
+ }
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onReceivedError=" + failingUrl);
+ mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl);
+ TraceEvent.end();
+ }
+
+ /**
+ * @see ContentViewClient#onReceivedTitle(String)
+ */
+ @Override
+ public void onReceivedTitle(String title) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onReceivedTitle");
+ mWebChromeClient.onReceivedTitle(mWebView, title);
+ }
+ TraceEvent.end();
+ }
+
+
+ /**
+ * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent)
+ */
+ @Override
+ public boolean shouldOverrideKeyEvent(KeyEvent event) {
+ // The check below is reflecting Clank's behavior and is a workaround for http://b/7697782.
+ // 1. The check for system key should be made in AwContents or ContentViewCore, before
+ // shouldOverrideKeyEvent() is called at all.
+ // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from
+ // dispatchKeyEvent().
+ if (!ContentViewClient.shouldPropagateKey(event.getKeyCode())) return true;
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "shouldOverrideKeyEvent");
+ boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
+ TraceEvent.end();
+ return result;
+ }
+
+
+ /**
+ * @see ContentViewClient#onStartContentIntent(Context, String)
+ * Callback when detecting a click on a content link.
+ */
+ // TODO: Delete this method when removed from base class.
+ public void onStartContentIntent(Context context, String contentUrl) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + contentUrl);
+ mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onGeolocationPermissionsShowPrompt");
+ mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onGeolocationPermissionsHidePrompt() {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onGeolocationPermissionsHidePrompt");
+ mWebChromeClient.onGeolocationPermissionsHidePrompt();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onPermissionRequest(AwPermissionRequest permissionRequest) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onPermissionRequest");
+ if (mOngoingPermissionRequests == null) {
+ mOngoingPermissionRequests =
+ new WeakHashMap<AwPermissionRequest, WeakReference<PermissionRequestAdapter>>();
+ }
+ PermissionRequestAdapter adapter = new PermissionRequestAdapter(permissionRequest);
+ mOngoingPermissionRequests.put(
+ permissionRequest, new WeakReference<PermissionRequestAdapter>(adapter));
+ mWebChromeClient.onPermissionRequest(adapter);
+ } else {
+ // By default, we deny the permission.
+ permissionRequest.deny();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onPermissionRequestCanceled(AwPermissionRequest permissionRequest) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null && mOngoingPermissionRequests != null) {
+ if (TRACE) Log.d(TAG, "onPermissionRequestCanceled");
+ WeakReference<PermissionRequestAdapter> weakRef =
+ mOngoingPermissionRequests.get(permissionRequest);
+ // We don't hold strong reference to PermissionRequestAdpater and don't expect the
+ // user only holds weak reference to it either, if so, user has no way to call
+ // grant()/deny(), and no need to be notified the cancellation of request.
+ if (weakRef != null) {
+ PermissionRequestAdapter adapter = weakRef.get();
+ if (adapter != null) mWebChromeClient.onPermissionRequestCanceled(adapter);
+ }
+ }
+ TraceEvent.end();
+ }
+
+ private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver {
+ private JsPromptResultReceiver mChromePromptResultReceiver;
+ private JsResultReceiver mChromeResultReceiver;
+ // We hold onto the JsPromptResult here, just to avoid the need to downcast
+ // in onJsResultComplete.
+ private final JsPromptResult mPromptResult = new JsPromptResult(this);
+
+ public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) {
+ mChromePromptResultReceiver = receiver;
+ }
+
+ public JsPromptResultReceiverAdapter(JsResultReceiver receiver) {
+ mChromeResultReceiver = receiver;
+ }
+
+ public JsPromptResult getPromptResult() {
+ return mPromptResult;
+ }
+
+ @Override
+ public void onJsResultComplete(JsResult result) {
+ if (mChromePromptResultReceiver != null) {
+ if (mPromptResult.getResult()) {
+ mChromePromptResultReceiver.confirm(mPromptResult.getStringResult());
+ } else {
+ mChromePromptResultReceiver.cancel();
+ }
+ } else {
+ if (mPromptResult.getResult()) {
+ mChromeResultReceiver.confirm();
+ } else {
+ mChromeResultReceiver.cancel();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void handleJsAlert(String url, String message, JsResultReceiver receiver) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ final JsPromptResult res =
+ new JsPromptResultReceiverAdapter(receiver).getPromptResult();
+ if (TRACE) Log.d(TAG, "onJsAlert");
+ if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) {
+ new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url)
+ .showDialog(mContext);
+ }
+ } else {
+ receiver.cancel();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ final JsPromptResult res =
+ new JsPromptResultReceiverAdapter(receiver).getPromptResult();
+ if (TRACE) Log.d(TAG, "onJsBeforeUnload");
+ if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) {
+ new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url)
+ .showDialog(mContext);
+ }
+ } else {
+ receiver.cancel();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void handleJsConfirm(String url, String message, JsResultReceiver receiver) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ final JsPromptResult res =
+ new JsPromptResultReceiverAdapter(receiver).getPromptResult();
+ if (TRACE) Log.d(TAG, "onJsConfirm");
+ if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) {
+ new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url)
+ .showDialog(mContext);
+ }
+ } else {
+ receiver.cancel();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void handleJsPrompt(String url, String message, String defaultValue,
+ JsPromptResultReceiver receiver) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ final JsPromptResult res =
+ new JsPromptResultReceiverAdapter(receiver).getPromptResult();
+ if (TRACE) Log.d(TAG, "onJsPrompt");
+ if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) {
+ new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url)
+ .showDialog(mContext);
+ }
+ } else {
+ receiver.cancel();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onReceivedHttpAuthRequest=" + host);
+ mWebViewClient.onReceivedHttpAuthRequest(mWebView,
+ new AwHttpAuthHandlerAdapter(handler), host, realm);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) {
+ SslErrorHandler handler = new SslErrorHandler() {
+ @Override
+ public void proceed() {
+ callback.onReceiveValue(true);
+ }
+ @Override
+ public void cancel() {
+ callback.onReceiveValue(false);
+ }
+ };
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onReceivedSslError");
+ mWebViewClient.onReceivedSslError(mWebView, handler, error);
+ TraceEvent.end();
+ }
+
+ private static class ClientCertRequestImpl extends ClientCertRequest {
+
+ final private AwContentsClientBridge.ClientCertificateRequestCallback mCallback;
+ final private String[] mKeyTypes;
+ final private Principal[] mPrincipals;
+ final private String mHost;
+ final private int mPort;
+
+ public ClientCertRequestImpl(
+ AwContentsClientBridge.ClientCertificateRequestCallback callback,
+ String[] keyTypes, Principal[] principals, String host, int port) {
+ mCallback = callback;
+ mKeyTypes = keyTypes;
+ mPrincipals = principals;
+ mHost = host;
+ mPort = port;
+ }
+
+ @Override
+ public String[] getKeyTypes() {
+ // This is already a copy of native argument, so return directly.
+ return mKeyTypes;
+ }
+
+ @Override
+ public Principal[] getPrincipals() {
+ // This is already a copy of native argument, so return directly.
+ return mPrincipals;
+ }
+
+ @Override
+ public String getHost() {
+ return mHost;
+ }
+
+ @Override
+ public int getPort() {
+ return mPort;
+ }
+
+ @Override
+ public void proceed(final PrivateKey privateKey, final X509Certificate[] chain) {
+ mCallback.proceed(privateKey, chain);
+ }
+
+ @Override
+ public void ignore() {
+ mCallback.ignore();
+ }
+
+ @Override
+ public void cancel() {
+ mCallback.cancel();
+ }
+ }
+
+ @Override
+ public void onReceivedClientCertRequest(
+ AwContentsClientBridge.ClientCertificateRequestCallback callback,
+ String[] keyTypes, Principal[] principals, String host, int port) {
+ if (TRACE) Log.d(TAG, "onReceivedClientCertRequest");
+ TraceEvent.begin();
+ final ClientCertRequestImpl request = new ClientCertRequestImpl(callback,
+ keyTypes, principals, host, port);
+ mWebViewClient.onReceivedClientCertRequest(mWebView, request);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onReceivedLoginRequest(String realm, String account, String args) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onReceivedLoginRequest=" + realm);
+ mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onFormResubmission(Message dontResend, Message resend) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onFormResubmission");
+ mWebViewClient.onFormResubmission(mWebView, dontResend, resend);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onDownloadStart(String url,
+ String userAgent,
+ String contentDisposition,
+ String mimeType,
+ long contentLength) {
+ if (mDownloadListener != null) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, "onDownloadStart");
+ mDownloadListener.onDownloadStart(url,
+ userAgent,
+ contentDisposition,
+ mimeType,
+ contentLength);
+ TraceEvent.end();
+ }
+ }
+
+ @Override
+ public void showFileChooser(final ValueCallback<String[]> uploadFileCallback,
+ final AwContentsClient.FileChooserParams fileChooserParams) {
+ if (mWebChromeClient == null) {
+ uploadFileCallback.onReceiveValue(null);
+ return;
+ }
+ TraceEvent.begin();
+ FileChooserParamsAdapter adapter = new FileChooserParamsAdapter(
+ fileChooserParams, mContext);
+ if (TRACE) Log.d(TAG, "showFileChooser");
+ ValueCallback<Uri[]> callbackAdapter = new ValueCallback<Uri[]>() {
+ private boolean mCompleted;
+ @Override
+ public void onReceiveValue(Uri[] uriList) {
+ if (mCompleted) {
+ throw new IllegalStateException("showFileChooser result was already called");
+ }
+ mCompleted = true;
+ String s[] = null;
+ if (uriList != null) {
+ s = new String[uriList.length];
+ for(int i = 0; i < uriList.length; i++) {
+ s[i] = uriList[i].toString();
+ }
+ }
+ uploadFileCallback.onReceiveValue(s);
+ }
+ };
+
+ // Invoke the new callback introduced in Lollipop. If the app handles
+ // it, we're done here.
+ if (mWebChromeClient.onShowFileChooser(mWebView, callbackAdapter, adapter)) {
+ return;
+ }
+
+ // If the app did not handle it and we are running on Lollipop or newer, then
+ // abort.
+ if (mContext.getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.LOLLIPOP) {
+ uploadFileCallback.onReceiveValue(null);
+ return;
+ }
+
+ // Otherwise, for older apps, attempt to invoke the legacy (hidden) API for
+ // backwards compatibility.
+ ValueCallback<Uri> innerCallback = new ValueCallback<Uri>() {
+ private boolean mCompleted;
+ @Override
+ public void onReceiveValue(Uri uri) {
+ if (mCompleted) {
+ throw new IllegalStateException("showFileChooser result was already called");
+ }
+ mCompleted = true;
+ uploadFileCallback.onReceiveValue(
+ uri == null ? null : new String[] { uri.toString() });
+ }
+ };
+ if (TRACE) Log.d(TAG, "openFileChooser");
+ mWebChromeClient.openFileChooser(innerCallback, fileChooserParams.acceptTypes,
+ fileChooserParams.capture ? "*" : "");
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onScaleChangedScaled(float oldScale, float newScale) {
+ TraceEvent.begin();
+ if (TRACE) Log.d(TAG, " onScaleChangedScaled");
+ mWebViewClient.onScaleChanged(mWebView, oldScale, newScale);
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onShowCustomView(View view, CustomViewCallback cb) {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onShowCustomView");
+ mWebChromeClient.onShowCustomView(view, cb);
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ public void onHideCustomView() {
+ TraceEvent.begin();
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "onHideCustomView");
+ mWebChromeClient.onHideCustomView();
+ }
+ TraceEvent.end();
+ }
+
+ @Override
+ protected View getVideoLoadingProgressView() {
+ TraceEvent.begin();
+ View result;
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "getVideoLoadingProgressView");
+ result = mWebChromeClient.getVideoLoadingProgressView();
+ } else {
+ result = null;
+ }
+ TraceEvent.end();
+ return result;
+ }
+
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+ TraceEvent.begin();
+ Bitmap result = null;
+ if (mWebChromeClient != null) {
+ if (TRACE) Log.d(TAG, "getDefaultVideoPoster");
+ result = mWebChromeClient.getDefaultVideoPoster();
+ }
+ if (result == null) {
+ // The ic_media_video_poster icon is transparent so we need to draw it on a gray
+ // background.
+ Bitmap poster = BitmapFactory.decodeResource(
+ mContext.getResources(),
+ R.drawable.ic_media_video_poster);
+ result = Bitmap.createBitmap(poster.getWidth(), poster.getHeight(), poster.getConfig());
+ result.eraseColor(Color.GRAY);
+ Canvas canvas = new Canvas(result);
+ canvas.drawBitmap(poster, 0f, 0f, null);
+ }
+ TraceEvent.end();
+ return result;
+ }
+
+ // TODO: Move to upstream.
+ private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler {
+ private AwHttpAuthHandler mAwHandler;
+
+ public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) {
+ mAwHandler = awHandler;
+ }
+
+ @Override
+ public void proceed(String username, String password) {
+ if (username == null) {
+ username = "";
+ }
+
+ if (password == null) {
+ password = "";
+ }
+ mAwHandler.proceed(username, password);
+ }
+
+ @Override
+ public void cancel() {
+ mAwHandler.cancel();
+ }
+
+ @Override
+ public boolean useHttpAuthUsernamePassword() {
+ return mAwHandler.isFirstAttempt();
+ }
+ }
+
+ // TODO: Move to the upstream once the PermissionRequest is part of SDK.
+ public static class PermissionRequestAdapter extends PermissionRequest {
+ // TODO: Move the below definitions to AwPermissionRequest.
+ private static long BITMASK_RESOURCE_VIDEO_CAPTURE = 1 << 1;
+ private static long BITMASK_RESOURCE_AUDIO_CAPTURE = 1 << 2;
+ private static long BITMASK_RESOURCE_PROTECTED_MEDIA_ID = 1 << 3;
+
+ public static long toAwPermissionResources(String[] resources) {
+ long result = 0;
+ for (String resource : resources) {
+ if (resource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
+ result |= BITMASK_RESOURCE_VIDEO_CAPTURE;
+ else if (resource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE))
+ result |= BITMASK_RESOURCE_AUDIO_CAPTURE;
+ else if (resource.equals(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID))
+ result |= BITMASK_RESOURCE_PROTECTED_MEDIA_ID;
+ }
+ return result;
+ }
+
+ private static String[] toPermissionResources(long resources) {
+ ArrayList<String> result = new ArrayList<String>();
+ if ((resources & BITMASK_RESOURCE_VIDEO_CAPTURE) != 0)
+ result.add(PermissionRequest.RESOURCE_VIDEO_CAPTURE);
+ if ((resources & BITMASK_RESOURCE_AUDIO_CAPTURE) != 0)
+ result.add(PermissionRequest.RESOURCE_AUDIO_CAPTURE);
+ if ((resources & BITMASK_RESOURCE_PROTECTED_MEDIA_ID) != 0)
+ result.add(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID);
+ String[] resource_array = new String[result.size()];
+ return result.toArray(resource_array);
+ }
+
+ private AwPermissionRequest mAwPermissionRequest;
+ private String[] mResources;
+
+ public PermissionRequestAdapter(AwPermissionRequest awPermissionRequest) {
+ assert awPermissionRequest != null;
+ mAwPermissionRequest = awPermissionRequest;
+ }
+
+ @Override
+ public Uri getOrigin() {
+ return mAwPermissionRequest.getOrigin();
+ }
+
+ @Override
+ public String[] getResources() {
+ synchronized (this) {
+ if (mResources == null) {
+ mResources = toPermissionResources(mAwPermissionRequest.getResources());
+ }
+ return mResources;
+ }
+ }
+
+ @Override
+ public void grant(String[] resources) {
+ long requestedResource = mAwPermissionRequest.getResources();
+ if ((requestedResource & toAwPermissionResources(resources)) == requestedResource)
+ mAwPermissionRequest.grant();
+ else
+ mAwPermissionRequest.deny();
+ }
+
+ @Override
+ public void deny() {
+ mAwPermissionRequest.deny();
+ }
+
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewDatabaseAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDatabaseAdapter.java
new file mode 100644
index 0000000..b7afa7c
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDatabaseAdapter.java
@@ -0,0 +1,57 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.content.Context;
+import android.webkit.WebViewDatabase;
+
+import org.chromium.android_webview.AwFormDatabase;
+import org.chromium.android_webview.HttpAuthDatabase;
+
+/**
+ * Chromium implementation of WebViewDatabase -- forwards calls to the
+ * chromium internal implementation.
+ */
+final class WebViewDatabaseAdapter extends WebViewDatabase {
+
+ private AwFormDatabase mFormDatabase;
+ private HttpAuthDatabase mHttpAuthDatabase;
+
+ public WebViewDatabaseAdapter(AwFormDatabase formDatabase, HttpAuthDatabase httpAuthDatabase) {
+ mFormDatabase = formDatabase;
+ mHttpAuthDatabase = httpAuthDatabase;
+ }
+
+ @Override
+ public boolean hasUsernamePassword() {
+ // This is a deprecated API: intentional no-op.
+ return false;
+ }
+
+ @Override
+ public void clearUsernamePassword() {
+ // This is a deprecated API: intentional no-op.
+ }
+
+ @Override
+ public boolean hasHttpAuthUsernamePassword() {
+ return mHttpAuthDatabase.hasHttpAuthUsernamePassword();
+ }
+
+ @Override
+ public void clearHttpAuthUsernamePassword() {
+ mHttpAuthDatabase.clearHttpAuthUsernamePassword();
+ }
+
+ @Override
+ public boolean hasFormData() {
+ return mFormDatabase.hasFormData();
+ }
+
+ @Override
+ public void clearFormData() {
+ mFormDatabase.clearFormData();
+ }
+}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java
new file mode 100644
index 0000000..8f713dc
--- /dev/null
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewDelegateFactory.java
@@ -0,0 +1,349 @@
+// Copyright 2014 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 com.android.webview.chromium;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.os.Trace;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.webkit.WebViewFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Factory class for {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s.
+ *
+ * <p>{@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s provide the same
+ * interface as {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} but without
+ * a dependency on the webkit class. Defining our own
+ * {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} in frameworks/webview
+ * allows the WebView apk to be binary compatible with the API 21 version of the framework, in
+ * which {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
+ * introduced.
+ *
+ * <p>The {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} interface and this
+ * factory class can be removed once we don't longer need to support WebView apk updates to devices
+ * running the API 21 version of the framework. At that point, we should use
+ * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} directly instead.
+ */
+class WebViewDelegateFactory {
+
+ /**
+ * Copy of {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}'s interface.
+ * See {@link WebViewDelegateFactory} for the reasons why this copy is needed.
+ */
+ interface WebViewDelegate {
+ /** @see android.webkit.WebViewDelegate.OnTraceEnabledChangeListener */
+ interface OnTraceEnabledChangeListener {
+ void onTraceEnabledChange(boolean enabled);
+ }
+
+ /** @see android.webkit.WebViewDelegate#setOnTraceEnabledChangeListener */
+ void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener);
+
+ /** @see android.webkit.WebViewDelegate#isTraceTagEnabled */
+ boolean isTraceTagEnabled();
+
+ /** @see android.webkit.WebViewDelegate#canInvokeDrawGlFunctor */
+ boolean canInvokeDrawGlFunctor(View containerView);
+
+ /** @see android.webkit.WebViewDelegate#invokeDrawGlFunctor */
+ void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
+ boolean waitForCompletion);
+
+ /** @see android.webkit.WebViewDelegate#callDrawGlFunction */
+ void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor);
+
+ /** @see android.webkit.WebViewDelegate#detachDrawGlFunctor */
+ void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor);
+
+ /** @see android.webkit.WebViewDelegate#getPackageId */
+ int getPackageId(Resources resources, String packageName);
+
+ /** @see android.webkit.WebViewDelegate#getApplication */
+ Application getApplication();
+
+ /** @see android.webkit.WebViewDelegate#getErrorString */
+ String getErrorString(Context context, int errorCode);
+
+ /** @see android.webkit.WebViewDelegate#addWebViewAssetPath */
+ void addWebViewAssetPath(Context context);
+ }
+
+ /**
+ * Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies
+ * requests to the given {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}.
+ *
+ * @return the created delegate
+ */
+ static WebViewDelegate createProxyDelegate(android.webkit.WebViewDelegate delegate) {
+ return new ProxyDelegate(delegate);
+ }
+
+ /**
+ * Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible
+ * with the API 21 version of the framework in which
+ * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
+ * introduced.
+ *
+ * @return the created delegate
+ */
+ static WebViewDelegate createApi21CompatibilityDelegate() {
+ return new Api21CompatibilityDelegate();
+ }
+
+ /**
+ * A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies requests
+ * to a {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}.
+ */
+ private static class ProxyDelegate implements WebViewDelegate {
+
+ android.webkit.WebViewDelegate delegate;
+
+ ProxyDelegate(android.webkit.WebViewDelegate delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) {
+ delegate.setOnTraceEnabledChangeListener(
+ new android.webkit.WebViewDelegate.OnTraceEnabledChangeListener() {
+ @Override
+ public void onTraceEnabledChange(boolean enabled) {
+ listener.onTraceEnabledChange(enabled);
+ ;
+ }
+ });
+ }
+
+ @Override
+ public boolean isTraceTagEnabled() {
+ return delegate.isTraceTagEnabled();
+ }
+
+ @Override
+ public boolean canInvokeDrawGlFunctor(View containerView) {
+ return delegate.canInvokeDrawGlFunctor(containerView);
+ }
+
+ @Override
+ public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
+ boolean waitForCompletion) {
+ delegate.invokeDrawGlFunctor(containerView, nativeDrawGLFunctor, waitForCompletion);
+ }
+
+ @Override
+ public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
+ delegate.callDrawGlFunction(canvas, nativeDrawGLFunctor);
+ }
+
+ @Override
+ public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) {
+ delegate.detachDrawGlFunctor(containerView, nativeDrawGLFunctor);
+ }
+
+ @Override
+ public int getPackageId(Resources resources, String packageName) {
+ return delegate.getPackageId(resources, packageName);
+ }
+
+ @Override
+ public Application getApplication() {
+ return delegate.getApplication();
+ }
+
+ @Override
+ public String getErrorString(Context context, int errorCode) {
+ return delegate.getErrorString(context, errorCode);
+ }
+
+ @Override
+ public void addWebViewAssetPath(Context context) {
+ delegate.addWebViewAssetPath(context);
+ }
+ }
+
+ /**
+ * A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible with the
+ * API 21 version of the framework in which
+ * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
+ * introduced.
+ *
+ * <p>This class implements the
+ * {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} functionality by using
+ * reflection to call into hidden frameworks APIs released in the API-21 version of the
+ * framework.
+ */
+ private static class Api21CompatibilityDelegate implements WebViewDelegate {
+ /** Copy of Trace.TRACE_TAG_WEBVIEW */
+ private final static long TRACE_TAG_WEBVIEW = 1L << 4;
+
+ /** Hidden APIs released in the API 21 version of the framework */
+ private final Method mIsTagEnabledMethod;
+ private final Method mAddChangeCallbackMethod;
+ private final Method mGetViewRootImplMethod;
+ private final Method mInvokeFunctorMethod;
+ private final Method mCallDrawGLFunctionMethod;
+ private final Method mDetachFunctorMethod;
+ private final Method mGetAssignedPackageIdentifiersMethod;
+ private final Method mAddAssetPathMethod;
+ private final Method mCurrentApplicationMethod;
+ private final Method mGetStringMethod;
+ private final Method mGetLoadedPackageInfoMethod;
+
+ Api21CompatibilityDelegate() {
+ try {
+ // Important: This reflection essentially defines a snapshot of some hidden APIs
+ // at version 21 of the framework for compatibility reasons, and the reflection
+ // should not be changed even if those hidden APIs change in future releases.
+ mIsTagEnabledMethod = Trace.class.getMethod("isTagEnabled", long.class);
+ mAddChangeCallbackMethod = Class.forName("android.os.SystemProperties")
+ .getMethod("addChangeCallback", Runnable.class);
+ mGetViewRootImplMethod = View.class.getMethod("getViewRootImpl");
+ mInvokeFunctorMethod = Class.forName("android.view.ViewRootImpl")
+ .getMethod("invokeFunctor", long.class, boolean.class);
+ mDetachFunctorMethod = Class.forName("android.view.ViewRootImpl")
+ .getMethod("detachFunctor", long.class);
+ mCallDrawGLFunctionMethod = Class.forName("android.view.HardwareCanvas")
+ .getMethod("callDrawGLFunction", long.class);
+ mGetAssignedPackageIdentifiersMethod = AssetManager.class.getMethod(
+ "getAssignedPackageIdentifiers");
+ mAddAssetPathMethod = AssetManager.class.getMethod(
+ "addAssetPath", String.class);
+ mCurrentApplicationMethod = Class.forName("android.app.ActivityThread")
+ .getMethod("currentApplication");
+ mGetStringMethod = Class.forName("android.net.http.ErrorStrings")
+ .getMethod("getString", int.class, Context.class);
+ mGetLoadedPackageInfoMethod = Class.forName("android.webkit.WebViewFactory")
+ .getMethod("getLoadedPackageInfo");
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) {
+ try {
+ mAddChangeCallbackMethod.invoke(null, new Runnable() {
+ @Override
+ public void run() {
+ listener.onTraceEnabledChange(isTraceTagEnabled());
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public boolean isTraceTagEnabled() {
+ try {
+ return ((Boolean) mIsTagEnabledMethod.invoke(null, TRACE_TAG_WEBVIEW));
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public boolean canInvokeDrawGlFunctor(View containerView) {
+ try {
+ Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
+ // viewRootImpl can be null during teardown when window is leaked.
+ return viewRootImpl != null;
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
+ boolean waitForCompletion) {
+ try {
+ Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
+ if (viewRootImpl != null) {
+ mInvokeFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor, waitForCompletion);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
+ try {
+ mCallDrawGLFunctionMethod.invoke(canvas, nativeDrawGLFunctor);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) {
+ try {
+ Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
+ if (viewRootImpl != null) {
+ mDetachFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public int getPackageId(Resources resources, String packageName) {
+ try {
+ SparseArray packageIdentifiers =
+ (SparseArray) mGetAssignedPackageIdentifiersMethod.invoke(
+ resources.getAssets());
+ for (int i = 0; i < packageIdentifiers.size(); i++) {
+ final String name = (String) packageIdentifiers.valueAt(i);
+
+ if (packageName.equals(name)) {
+ return packageIdentifiers.keyAt(i);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ throw new RuntimeException("Package not found: " + packageName);
+ }
+
+ @Override
+ public Application getApplication() {
+ try {
+ return (Application) mCurrentApplicationMethod.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public String getErrorString(Context context, int errorCode) {
+ try {
+ return (String) mGetStringMethod.invoke(null, errorCode, context);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+
+ @Override
+ public void addWebViewAssetPath(Context context) {
+ try {
+ PackageInfo info = (PackageInfo) mGetLoadedPackageInfoMethod.invoke(null);
+ mAddAssetPathMethod.invoke(context.getAssets(), info.applicationInfo.sourceDir);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid reflection", e);
+ }
+ }
+ }
+}
+