summaryrefslogtreecommitdiff
path: root/chromium/weblayer/browser/java
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 17:21:03 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 16:25:15 +0000
commitc551f43206405019121bd2b2c93714319a0a3300 (patch)
tree1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/weblayer/browser/java
parent7961cea6d1041e3e454dae6a1da660b453efd238 (diff)
downloadqtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/weblayer/browser/java')
-rw-r--r--chromium/weblayer/browser/java/BUILD.gn154
-rw-r--r--chromium/weblayer/browser/java/DEPS4
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java63
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java143
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java103
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java192
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java50
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java155
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java53
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java490
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java695
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java276
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java53
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java51
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationHandler.java99
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java38
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java47
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java70
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridge.java55
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridgeImpl.java11
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/MinidumpUploader.java188
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java154
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java108
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java70
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/OWNERS2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java76
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java28
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/RemoteFragmentImpl.java247
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java49
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java290
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java351
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebContentsGestureStateTracker.java110
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java77
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java253
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl20
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl14
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl16
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl11
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/APICallException.java19
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java8
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java17
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl33
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl13
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl13
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IChildProcessService.aidl16
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IClientNavigation.aidl (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IClientNavigation.aidl)2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterController.aidl18
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterControllerClient.aidl16
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl12
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl13
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl17
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigation.aidl)10
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl)6
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl)15
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IObjectWrapper.aidl (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IObjectWrapper.aidl)2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl16
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl30
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl35
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl34
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl19
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl54
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl26
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java22
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigationState.java20
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NewTabType.java20
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ObjectWrapper.java (renamed from chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/ObjectWrapper.java)2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java12
67 files changed, 4963 insertions, 423 deletions
diff --git a/chromium/weblayer/browser/java/BUILD.gn b/chromium/weblayer/browser/java/BUILD.gn
index 24403c5be58..6b5e736a49f 100644
--- a/chromium/weblayer/browser/java/BUILD.gn
+++ b/chromium/weblayer/browser/java/BUILD.gn
@@ -4,54 +4,170 @@
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
+import("//weblayer/variables.gni")
+
+android_resources("weblayer_resources") {
+ resource_dirs = []
+ custom_package = "org.chromium.weblayer_private"
+}
+
+generate_locale_config_srcjar("weblayer_locale_config") {
+ java_package = weblayer_locale_config_java_package
+}
+
+java_cpp_enum("generated_enums") {
+ sources = [
+ "//weblayer/public/navigation.h",
+ "//weblayer/public/new_tab_delegate.h",
+ "//weblayer/public/profile.h",
+ ]
+}
android_library("java") {
java_files = [
- "org/chromium/weblayer_private/BrowserControllerImpl.java",
- "org/chromium/weblayer_private/BrowserObserverProxy.java",
+ "org/chromium/weblayer_private/BrowserImpl.java",
+ "org/chromium/weblayer_private/BrowserFragmentImpl.java",
+ "org/chromium/weblayer_private/BrowserViewController.java",
+ "org/chromium/weblayer_private/ChildProcessServiceImpl.java",
+ "org/chromium/weblayer_private/ContentView.java",
+ "org/chromium/weblayer_private/ContentViewRenderView.java",
+ "org/chromium/weblayer_private/CrashReporterControllerImpl.java",
+ "org/chromium/weblayer_private/DownloadCallbackProxy.java",
+ "org/chromium/weblayer_private/ErrorPageCallbackProxy.java",
+ "org/chromium/weblayer_private/ExternalNavigationHandler.java",
+ "org/chromium/weblayer_private/ActionModeCallback.java",
+ "org/chromium/weblayer_private/FullscreenCallbackProxy.java",
+ "org/chromium/weblayer_private/MinidumpUploader.java",
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
+ "org/chromium/weblayer_private/NewTabCallbackProxy.java",
"org/chromium/weblayer_private/ProfileImpl.java",
+ "org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java",
+ "org/chromium/weblayer_private/FragmentWindowAndroid.java",
+ "org/chromium/weblayer_private/ProfileManager.java",
+ "org/chromium/weblayer_private/RemoteFragmentImpl.java",
+ "org/chromium/weblayer_private/TabCallbackProxy.java",
+ "org/chromium/weblayer_private/TabImpl.java",
+ "org/chromium/weblayer_private/TopControlsContainerView.java",
+ "org/chromium/weblayer_private/WebContentsGestureStateTracker.java",
+ "org/chromium/weblayer_private/WebLayerFactoryImpl.java",
"org/chromium/weblayer_private/WebLayerImpl.java",
]
deps = [
- ":client_java",
+ ":gms_bridge_java",
+ ":interfaces_java",
+ ":weblayer_resources",
"//base:base_java",
- "//components/embedder_support/android:content_view_java",
- "//components/embedder_support/android:view_java",
+ "//base:jni_java",
+ "//components/crash/android:handler_java",
+ "//components/crash/android:java",
+ "//components/embedder_support/android:application_java",
+ "//components/embedder_support/android:web_contents_delegate_java",
+ "//components/minidump_uploader:minidump_uploader_java",
+ "//components/version_info/android:version_constants_java",
"//content/public/android:content_java",
+ "//third_party/android_deps:com_android_support_support_compat_java",
"//ui/android:ui_java",
]
+ srcjar_deps = [
+ ":generated_enums",
+ ":weblayer_locale_config",
+ ]
+ jar_excluded_patterns = [ "*/LocaleConfig.class" ]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+
+ # Needed for android.webkit.WebView(Delegate|Factory)
+ alternative_android_sdk_dep =
+ "//third_party/android_sdk:public_framework_system_java"
}
generate_jni("jni") {
sources = [
- "org/chromium/weblayer_private/BrowserControllerImpl.java",
- "org/chromium/weblayer_private/BrowserObserverProxy.java",
+ "org/chromium/weblayer_private/ContentViewRenderView.java",
+ "org/chromium/weblayer_private/DownloadCallbackProxy.java",
+ "org/chromium/weblayer_private/ErrorPageCallbackProxy.java",
+ "org/chromium/weblayer_private/ExternalNavigationHandler.java",
+ "org/chromium/weblayer_private/FullscreenCallbackProxy.java",
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
+ "org/chromium/weblayer_private/NewTabCallbackProxy.java",
"org/chromium/weblayer_private/ProfileImpl.java",
+ "org/chromium/weblayer_private/TabCallbackProxy.java",
+ "org/chromium/weblayer_private/TabImpl.java",
+ "org/chromium/weblayer_private/TopControlsContainerView.java",
+ "org/chromium/weblayer_private/WebLayerImpl.java",
]
}
-android_library("client_java") {
- java_files = [ "org/chromium/weblayer_private/aidl/ObjectWrapper.java" ]
+android_library("interfaces_java") {
+ java_files = [
+ "org/chromium/weblayer_private/interfaces/APICallException.java",
+ "org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java",
+ "org/chromium/weblayer_private/interfaces/BrowsingDataType.java",
+ "org/chromium/weblayer_private/interfaces/LoadError.java",
+ "org/chromium/weblayer_private/interfaces/NavigationState.java",
+ "org/chromium/weblayer_private/interfaces/NewTabType.java",
+ "org/chromium/weblayer_private/interfaces/ObjectWrapper.java",
+ "org/chromium/weblayer_private/interfaces/WebLayerVersion.java",
+ ]
+
+ deps = [
+ "//third_party/android_deps:androidx_annotation_annotation_java",
+ ]
srcjar_deps = [ ":aidl" ]
}
+# Separate target to allow for a dependency on GmsCore without pulling in all of
+# java classes. It compiles the abstract class; implementations are compiled
+# separately.
+android_library("gms_bridge_java") {
+ java_files = [
+ "org/chromium/weblayer_private/GmsBridge.java",
+ "org/chromium/weblayer_private/GmsBridgeImpl.java",
+ ]
+
+ deps = [
+ "//base:base_java",
+ ]
+
+ # The appropriate .class file will be loaded via a dependency to a library
+ # like :gms_bridge_upstream_impl_java below.
+ jar_excluded_patterns = [ "*/GmsBridgeImpl.class" ]
+}
+
+# This target compiles the implementation of GmsBridge for public targets.
+android_library("gms_bridge_upstream_impl_java") {
+ java_files = [ "org/chromium/weblayer_private/GmsBridgeImpl.java" ]
+ deps = [
+ ":gms_bridge_java",
+ ]
+}
+
android_aidl("aidl") {
- import_include = [ "org/chromium/weblayer_private/aidl" ]
+ import_include = [ "." ]
sources = [
- "org/chromium/weblayer_private/aidl/IBrowserController.aidl",
- "org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl",
- "org/chromium/weblayer_private/aidl/IClientNavigation.aidl",
- "org/chromium/weblayer_private/aidl/INavigation.aidl",
- "org/chromium/weblayer_private/aidl/INavigationController.aidl",
- "org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl",
- "org/chromium/weblayer_private/aidl/IObjectWrapper.aidl",
- "org/chromium/weblayer_private/aidl/IProfile.aidl",
- "org/chromium/weblayer_private/aidl/IWebLayer.aidl",
+ "org/chromium/weblayer_private/interfaces/IBrowser.aidl",
+ "org/chromium/weblayer_private/interfaces/IBrowserClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl",
+ "org/chromium/weblayer_private/interfaces/IChildProcessService.aidl",
+ "org/chromium/weblayer_private/interfaces/IClientNavigation.aidl",
+ "org/chromium/weblayer_private/interfaces/ICrashReporterController.aidl",
+ "org/chromium/weblayer_private/interfaces/ICrashReporterControllerClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl",
+ "org/chromium/weblayer_private/interfaces/INavigation.aidl",
+ "org/chromium/weblayer_private/interfaces/INavigationController.aidl",
+ "org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IObjectWrapper.aidl",
+ "org/chromium/weblayer_private/interfaces/IProfile.aidl",
+ "org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl",
+ "org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl",
+ "org/chromium/weblayer_private/interfaces/ITab.aidl",
+ "org/chromium/weblayer_private/interfaces/ITabClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IWebLayer.aidl",
+ "org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl",
]
}
diff --git a/chromium/weblayer/browser/java/DEPS b/chromium/weblayer/browser/java/DEPS
new file mode 100644
index 00000000000..0785845d9c6
--- /dev/null
+++ b/chromium/weblayer/browser/java/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/crash/android/java",
+ "+components/minidump_uploader",
+]
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java
new file mode 100644
index 00000000000..6b1103e7324
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.app.SearchManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.chromium.base.PackageManagerUtils;
+import org.chromium.content_public.browser.ActionModeCallbackHelper;
+import org.chromium.content_public.browser.SelectionPopupController;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * A class that handles selection action mode for WebLayer.
+ */
+public final class ActionModeCallback implements ActionMode.Callback {
+ private final ActionModeCallbackHelper mHelper;
+
+ public ActionModeCallback(WebContents webContents) {
+ mHelper =
+ SelectionPopupController.fromWebContents(webContents).getActionModeCallbackHelper();
+ }
+
+ @Override
+ public final boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ int allowedActionModes = ActionModeCallbackHelper.MENU_ITEM_PROCESS_TEXT
+ | ActionModeCallbackHelper.MENU_ITEM_SHARE;
+ if (isWebSearchAvailable()) {
+ allowedActionModes |= ActionModeCallbackHelper.MENU_ITEM_WEB_SEARCH;
+ }
+ mHelper.setAllowedMenuItems(allowedActionModes);
+ mHelper.onCreateActionMode(mode, menu);
+ return true;
+ }
+
+ private boolean isWebSearchAvailable() {
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
+ return !PackageManagerUtils.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ .isEmpty();
+ }
+
+ @Override
+ public final boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return mHelper.onPrepareActionMode(mode, menu);
+ }
+
+ @Override
+ public final boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mHelper.onActionItemClicked(mode, item);
+ }
+
+ @Override
+ public final void onDestroyActionMode(ActionMode mode) {
+ mHelper.onDestroyActionMode();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
deleted file mode 100644
index cd9ee225041..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private;
-
-import android.content.Context;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.LinearLayout;
-
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.components.embedder_support.view.ContentView;
-import org.chromium.components.embedder_support.view.ContentViewRenderView;
-import org.chromium.content_public.browser.ViewEventSink;
-import org.chromium.content_public.browser.WebContents;
-import org.chromium.ui.base.ActivityWindowAndroid;
-import org.chromium.ui.base.ViewAndroidDelegate;
-import org.chromium.weblayer_private.aidl.IBrowserController;
-import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
-import org.chromium.weblayer_private.aidl.INavigationControllerClient;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.ObjectWrapper;
-
-@JNINamespace("weblayer")
-public final class BrowserControllerImpl extends IBrowserController.Stub {
- private long mNativeBrowserController;
-
- private ActivityWindowAndroid mWindowAndroid;
- // This is set as the content view of the activity. It contains mContentViewRenderView.
- private LinearLayout mLinearLayout;
- // This is parented to mLinearLayout.
- private ContentViewRenderView mContentViewRenderView;
- // One of these is needed per WebContents.
- private ContentView mContentView;
- private ProfileImpl mProfile;
- private WebContents mWebContents;
- private BrowserObserverProxy mBrowserObserverProxy;
- private NavigationControllerImpl mNavigationController;
- private View mTopView;
-
- private static class InternalAccessDelegateImpl
- implements ViewEventSink.InternalAccessDelegate {
- @Override
- public boolean super_onKeyUp(int keyCode, KeyEvent event) {
- return false;
- }
-
- @Override
- public boolean super_dispatchKeyEvent(KeyEvent event) {
- return false;
- }
-
- @Override
- public boolean super_onGenericMotionEvent(MotionEvent event) {
- return false;
- }
-
- @Override
- public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {}
- }
-
- public BrowserControllerImpl(Context context, ProfileImpl profile) {
- mProfile = profile;
-
- mLinearLayout = new LinearLayout(context);
- mLinearLayout.setOrientation(LinearLayout.VERTICAL);
-
- mWindowAndroid = new ActivityWindowAndroid(context);
- mContentViewRenderView = new ContentViewRenderView(context);
- mWindowAndroid.setAnimationPlaceholderView(mContentViewRenderView.getSurfaceView());
-
- mContentViewRenderView.onNativeLibraryLoaded(mWindowAndroid);
-
- mNativeBrowserController = nativeCreateBrowserController(profile.getNativeProfile());
- mWebContents = nativeGetWebContents(mNativeBrowserController);
- mWebContents.initialize("", ViewAndroidDelegate.createBasicDelegate(mContentViewRenderView),
- new InternalAccessDelegateImpl(), mWindowAndroid,
- WebContents.createDefaultInternalsHolder());
-
- mContentViewRenderView.setCurrentWebContents(mWebContents);
- mLinearLayout.addView(mContentViewRenderView,
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1f));
-
- mContentView = ContentView.createContentView(context, mWebContents);
- mContentViewRenderView.addView(mContentView,
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1f));
-
- mWebContents.onShow();
- }
-
- long getNativeBrowserController() {
- return mNativeBrowserController;
- }
-
- @Override
- public NavigationControllerImpl createNavigationController(INavigationControllerClient client) {
- // This should only be called once.
- assert mNavigationController == null;
- mNavigationController = new NavigationControllerImpl(this, client);
- return mNavigationController;
- }
-
- @Override
- public void setClient(IBrowserControllerClient client) {
- mBrowserObserverProxy = new BrowserObserverProxy(mNativeBrowserController, client);
- }
-
- @Override
- public void destroy() {
- if (mBrowserObserverProxy != null) mBrowserObserverProxy.destroy();
- mBrowserObserverProxy = null;
- mNavigationController = null;
- nativeDeleteBrowserController(mNativeBrowserController);
- mNativeBrowserController = 0;
- }
-
- @Override
- public void setTopView(IObjectWrapper viewWrapper) {
- View view = ObjectWrapper.unwrap(viewWrapper, View.class);
- if (mTopView == view) return;
- if (mTopView != null) mLinearLayout.removeView(mTopView);
- mTopView = view;
- if (mTopView != null) {
- mLinearLayout.addView(mTopView, 0,
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 0f));
- }
- }
-
- @Override
- public IObjectWrapper onCreateView() {
- return ObjectWrapper.wrap(mLinearLayout);
- }
-
- private static native long nativeCreateBrowserController(long profile);
- private static native void nativeDeleteBrowserController(long browserController);
- private native WebContents nativeGetWebContents(long nativeBrowserControllerImpl);
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
new file mode 100644
index 00000000000..7e0e6477aa2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
+import org.chromium.weblayer_private.interfaces.BrowserFragmentArgs;
+import org.chromium.weblayer_private.interfaces.IBrowser;
+import org.chromium.weblayer_private.interfaces.IBrowserFragment;
+import org.chromium.weblayer_private.interfaces.IRemoteFragment;
+import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
+
+/**
+ * Implementation of RemoteFragmentImpl which forwards logic to BrowserImpl.
+ */
+public class BrowserFragmentImpl extends RemoteFragmentImpl {
+ private final ProfileImpl mProfile;
+
+ private BrowserImpl mBrowser;
+ private Context mContext;
+
+ public BrowserFragmentImpl(
+ ProfileManager profileManager, IRemoteFragmentClient client, Bundle fragmentArgs) {
+ super(client);
+ mProfile =
+ profileManager.getProfile(fragmentArgs.getString(BrowserFragmentArgs.PROFILE_NAME));
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mContext = ClassLoaderContextWrapperFactory.get(context);
+ if (mBrowser != null) { // On first creation, onAttach is called before onCreate
+ mBrowser.onFragmentAttached(mContext, new FragmentWindowAndroid(mContext, this));
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mBrowser = new BrowserImpl(mProfile, savedInstanceState);
+ if (mContext != null) {
+ mBrowser.onFragmentAttached(mContext, new FragmentWindowAndroid(mContext, this));
+ }
+ }
+
+ @Override
+ public View onCreateView() {
+ return mBrowser.getFragmentView();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mBrowser.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ mBrowser.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mBrowser.destroy();
+ mBrowser = null;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ // mBrowser != null if fragment is retained, otherwise onDestroy is called first.
+ if (mBrowser != null) {
+ mBrowser.onFragmentDetached();
+ }
+ mContext = null;
+ }
+
+ public IBrowserFragment asIBrowserFragment() {
+ return new IBrowserFragment.Stub() {
+ @Override
+ public IRemoteFragment asRemoteFragment() {
+ return BrowserFragmentImpl.this;
+ }
+
+ @Override
+ public IBrowser getBrowser() {
+ if (mBrowser == null) {
+ throw new RuntimeException("Browser is available only between"
+ + " BrowserFragment's onCreate() and onDestroy().");
+ }
+ return mBrowser;
+ }
+ };
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
new file mode 100644
index 00000000000..08449086bee
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -0,0 +1,192 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.ValueCallback;
+
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.IBrowser;
+import org.chromium.weblayer_private.interfaces.IBrowserClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IProfile;
+import org.chromium.weblayer_private.interfaces.ITab;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link IBrowser}.
+ */
+public class BrowserImpl extends IBrowser.Stub {
+ private final ProfileImpl mProfile;
+ private BrowserViewController mViewController;
+ private FragmentWindowAndroid mWindowAndroid;
+ private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
+ private IBrowserClient mClient;
+
+ public BrowserImpl(ProfileImpl profile, Bundle savedInstanceState) {
+ mProfile = profile;
+ // Restore tabs etc from savedInstanceState here.
+ }
+
+ public WindowAndroid getWindowAndroid() {
+ return mWindowAndroid;
+ }
+
+ public ViewGroup getViewAndroidDelegateContainerView() {
+ return mViewController.getContentView();
+ }
+
+ public void onFragmentAttached(Context context, FragmentWindowAndroid windowAndroid) {
+ mWindowAndroid = windowAndroid;
+ mViewController = new BrowserViewController(context, windowAndroid);
+ TabImpl tab = new TabImpl(mProfile, windowAndroid);
+ addTab(tab);
+ boolean set_active_result = setActiveTab(tab);
+ assert set_active_result;
+ }
+
+ public void onFragmentDetached() {
+ destroy(); // For now we don't retain anything between detach and attach.
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mWindowAndroid != null) {
+ mWindowAndroid.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ if (mWindowAndroid != null) {
+ mWindowAndroid.handlePermissionResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ @Override
+ public void setTopView(IObjectWrapper viewWrapper) {
+ getViewController().setTopView(ObjectWrapper.unwrap(viewWrapper, View.class));
+ }
+
+ @Override
+ public void setSupportsEmbedding(boolean enable, IObjectWrapper valueCallback) {
+ getViewController().setSupportsEmbedding(enable,
+ (ValueCallback<Boolean>) ObjectWrapper.unwrap(valueCallback, ValueCallback.class));
+ }
+
+ public BrowserViewController getViewController() {
+ if (mViewController == null) {
+ throw new RuntimeException("Currently Tab requires Activity context, so "
+ + "it exists only while BrowserFragment is attached to an Activity");
+ }
+ return mViewController;
+ }
+
+ @Override
+ public IProfile getProfile() {
+ return mProfile;
+ }
+
+ @Override
+ public void addTab(ITab iTab) {
+ TabImpl tab = (TabImpl) iTab;
+ if (tab.getBrowser() == this) return;
+ addTabImpl(tab);
+ }
+
+ private void addTabImpl(TabImpl tab) {
+ // Null case is only during initial creation.
+ if (tab.getBrowser() != this && tab.getBrowser() != null) {
+ tab.getBrowser().detachTab(tab);
+ }
+ mTabs.add(tab);
+ tab.attachToBrowser(this);
+ try {
+ if (mClient != null) mClient.onTabAdded(tab);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void detachTab(ITab iTab) {
+ TabImpl tab = (TabImpl) iTab;
+ if (tab.getBrowser() != this) return;
+ if (getActiveTab() == tab) setActiveTab(null);
+ mTabs.remove(tab);
+ try {
+ if (mClient != null) mClient.onTabRemoved(tab.getId());
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ // This doesn't reset state on TabImpl as |browser| is either about to be
+ // destroyed, or switching to a different fragment.
+ }
+
+ @Override
+ public boolean setActiveTab(ITab controller) {
+ TabImpl tab = (TabImpl) controller;
+ if (tab != null && tab.getBrowser() != this) return false;
+ mViewController.setActiveTab(tab);
+ try {
+ if (mClient != null) {
+ mClient.onActiveTabChanged(tab != null ? tab.getId() : 0);
+ }
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ return true;
+ }
+
+ public TabImpl getActiveTab() {
+ return mViewController.getTab();
+ }
+
+ @Override
+ public List getTabs() {
+ return new ArrayList(mTabs);
+ }
+
+ @Override
+ public int getActiveTabId() {
+ return getActiveTab() != null ? getActiveTab().getId() : 0;
+ }
+
+ @Override
+ public void setClient(IBrowserClient client) {
+ mClient = client;
+ }
+
+ @Override
+ public void destroyTab(ITab iTab) {
+ detachTab(iTab);
+ ((TabImpl) iTab).destroy();
+ }
+
+ public View getFragmentView() {
+ return getViewController().getView();
+ }
+
+ public void destroy() {
+ if (mViewController != null) {
+ mViewController.destroy();
+ for (TabImpl tab : mTabs) {
+ tab.destroy();
+ }
+ mViewController = null;
+ }
+ if (mWindowAndroid != null) {
+ mWindowAndroid.destroy();
+ mWindowAndroid = null;
+ }
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
deleted file mode 100644
index 0cb684fce29..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private;
-
-import android.os.RemoteException;
-import android.util.AndroidRuntimeException;
-
-import org.chromium.base.Log;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
-
-/**
- * Owns the c++ BrowserObserverProxy class, which is responsible for forwarding all
- * BrowserObserver calls to this class, which in turn forwards to the BrowserControllerClient.
- * To avoid unnecessary IPC only one BrowserObserverProxy is created per BrowserController.
- */
-@JNINamespace("weblayer")
-public final class BrowserObserverProxy {
- private static final String TAG = "WL_BObserverProxy";
-
- private long mNativeBrowserObserverProxy;
- private IBrowserControllerClient mClient;
-
- BrowserObserverProxy(long browserController, IBrowserControllerClient client) {
- mClient = client;
- mNativeBrowserObserverProxy = nativeCreateBrowserObsererProxy(this, browserController);
- }
-
- public void destroy() {
- nativeDeleteBrowserObserverProxy(mNativeBrowserObserverProxy);
- mNativeBrowserObserverProxy = 0;
- }
-
- @CalledByNative
- private void displayURLChanged(String string) {
- try {
- mClient.displayURLChanged(string);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call displayURLChanged.", e);
- throw new AndroidRuntimeException(e);
- }
- }
-
- private static native long nativeCreateBrowserObsererProxy(
- BrowserObserverProxy proxy, long browserController);
- private static native void nativeDeleteBrowserObserverProxy(long proxy);
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
new file mode 100644
index 00000000000..abda36312e4
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
@@ -0,0 +1,155 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.webkit.ValueCallback;
+import android.widget.FrameLayout;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ViewAndroidDelegate;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * BrowserViewController controls the set of Views needed to show the WebContents.
+ */
+@JNINamespace("weblayer")
+public final class BrowserViewController
+ implements TopControlsContainerView.Listener,
+ WebContentsGestureStateTracker.OnGestureStateChangedListener {
+ private final ContentViewRenderView mContentViewRenderView;
+ private final ContentView mContentView;
+ // Child of mContentViewRenderView, holds top-view from client.
+ private final TopControlsContainerView mTopControlsContainerView;
+
+ private TabImpl mTab;
+
+ private WebContentsGestureStateTracker mGestureStateTracker;
+
+ /**
+ * The value of mCachedDoBrowserControlsShrinkRendererSize is set when
+ * WebContentsGestureStateTracker begins a gesture. This is necessary as the values should only
+ * change once a gesture is no longer under way.
+ */
+ private boolean mCachedDoBrowserControlsShrinkRendererSize;
+
+ public BrowserViewController(Context context, WindowAndroid windowAndroid) {
+ mContentViewRenderView = new ContentViewRenderView(context);
+
+ mContentViewRenderView.onNativeLibraryLoaded(
+ windowAndroid, ContentViewRenderView.MODE_SURFACE_VIEW);
+ mTopControlsContainerView =
+ new TopControlsContainerView(context, mContentViewRenderView, this);
+ mContentView = ContentView.createContentView(
+ context, mTopControlsContainerView.getEventOffsetHandler());
+ ViewAndroidDelegate viewAndroidDelegate = new ViewAndroidDelegate(mContentView) {
+ @Override
+ public void onTopControlsChanged(int topControlsOffsetY, int topContentOffsetY) {
+ mTopControlsContainerView.onTopControlsChanged(
+ topControlsOffsetY, topContentOffsetY);
+ }
+ };
+ mContentViewRenderView.addView(mContentView,
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY));
+ mContentView.addView(mTopControlsContainerView,
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+ Gravity.FILL_HORIZONTAL | Gravity.TOP));
+ }
+
+ public void destroy() {
+ setActiveTab(null);
+ mTopControlsContainerView.destroy();
+ mContentViewRenderView.destroy();
+ }
+
+ /** Returns top-level View this Controller works with */
+ public View getView() {
+ return mContentViewRenderView;
+ }
+
+ public ViewGroup getContentView() {
+ return mContentView;
+ }
+
+ public void setActiveTab(TabImpl tab) {
+ if (tab == mTab) return;
+
+ if (mTab != null) {
+ mTab.onDidLoseActive();
+ // WebContentsGestureStateTracker is relatively cheap, easier to destroy rather than
+ // update WebContents.
+ mGestureStateTracker.destroy();
+ mGestureStateTracker = null;
+ }
+ mTab = tab;
+ WebContents webContents = mTab != null ? mTab.getWebContents() : null;
+ // Create the WebContentsGestureStateTracker before setting the WebContents on
+ // the views as they may call back to this class.
+ if (mTab != null) {
+ mGestureStateTracker =
+ new WebContentsGestureStateTracker(mContentView, webContents, this);
+ }
+ mContentView.setWebContents(webContents);
+ mContentViewRenderView.setWebContents(webContents);
+ mTopControlsContainerView.setWebContents(webContents);
+ if (mTab != null) {
+ mTab.onDidGainActive(mTopControlsContainerView.getNativeHandle());
+ mContentView.requestFocus();
+ }
+ }
+
+ public TabImpl getTab() {
+ return mTab;
+ }
+
+ public void setTopView(View view) {
+ mTopControlsContainerView.setView(view);
+ }
+
+ @Override
+ public void onTopControlsCompletelyShownOrHidden() {
+ adjustWebContentsHeightIfNecessary();
+ }
+
+ @Override
+ public void onGestureStateChanged() {
+ if (mGestureStateTracker.isInGestureOrScroll()) {
+ mCachedDoBrowserControlsShrinkRendererSize =
+ mTopControlsContainerView.isTopControlVisible();
+ }
+ adjustWebContentsHeightIfNecessary();
+ }
+
+ private void adjustWebContentsHeightIfNecessary() {
+ if (mGestureStateTracker.isInGestureOrScroll()
+ || !mTopControlsContainerView.isTopControlsCompletelyShownOrHidden()) {
+ return;
+ }
+ mContentViewRenderView.setWebContentsHeightDelta(
+ mTopControlsContainerView.getTopContentOffset());
+ }
+
+ public void setSupportsEmbedding(boolean enable, ValueCallback<Boolean> callback) {
+ mContentViewRenderView.requestMode(enable ? ContentViewRenderView.MODE_TEXTURE_VIEW
+ : ContentViewRenderView.MODE_SURFACE_VIEW,
+ callback);
+ }
+
+ public void onTopControlsChanged(int topControlsOffsetY, int topContentOffsetY) {
+ mTopControlsContainerView.onTopControlsChanged(topControlsOffsetY, topContentOffsetY);
+ }
+
+ public boolean doBrowserControlsShrinkRendererSize() {
+ return (mGestureStateTracker.isInGestureOrScroll())
+ ? mCachedDoBrowserControlsShrinkRendererSize
+ : mTopControlsContainerView.isTopControlVisible();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java
new file mode 100644
index 00000000000..ebfecb29e94
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.base.process_launcher.ChildProcessService;
+import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
+import org.chromium.content_public.app.ChildProcessServiceFactory;
+import org.chromium.weblayer_private.interfaces.IChildProcessService;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+/**
+ * Implementation of IChildProcessService.
+ */
+@UsedByReflection("WebLayer")
+public final class ChildProcessServiceImpl extends IChildProcessService.Stub {
+ private ChildProcessService mService;
+
+ @UsedByReflection("WebLayer")
+ public static IBinder create(Service service, Context appContext) {
+ // Wrap the app context so that it can be used to load WebLayer implementation classes.
+ appContext = ClassLoaderContextWrapperFactory.get(appContext);
+ return new ChildProcessServiceImpl(service, appContext);
+ }
+
+ @Override
+ public void onCreate() {
+ mService.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ mService.onDestroy();
+ mService = null;
+ }
+
+ @Override
+ public IObjectWrapper onBind(IObjectWrapper intent) {
+ return ObjectWrapper.wrap(mService.onBind(ObjectWrapper.unwrap(intent, Intent.class)));
+ }
+
+ private ChildProcessServiceImpl(Service service, Context context) {
+ mService = ChildProcessServiceFactory.create(service, context);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
new file mode 100644
index 00000000000..65ff96fd0f8
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
@@ -0,0 +1,490 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.DragEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnSystemUiVisibilityChangeListener;
+import android.view.ViewGroup.OnHierarchyChangeListener;
+import android.view.ViewStructure;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.FrameLayout;
+
+import org.chromium.base.ObserverList;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.compat.ApiHelperForO;
+import org.chromium.content_public.browser.ImeAdapter;
+import org.chromium.content_public.browser.RenderCoordinates;
+import org.chromium.content_public.browser.SmartClipProvider;
+import org.chromium.content_public.browser.ViewEventSink;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsAccessibility;
+import org.chromium.ui.base.EventForwarder;
+import org.chromium.ui.base.EventOffsetHandler;
+
+/**
+ * The containing view for {@link WebContents} that exists in the Android UI hierarchy and exposes
+ * the various {@link View} functionality to it.
+ */
+public class ContentView extends FrameLayout
+ implements ViewEventSink.InternalAccessDelegate, SmartClipProvider,
+ OnHierarchyChangeListener, OnSystemUiVisibilityChangeListener {
+ private static final String TAG = "ContentView";
+
+ // Default value to signal that the ContentView's size need not be overridden.
+ public static final int DEFAULT_MEASURE_SPEC =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+ private WebContents mWebContents;
+ private final ObserverList<OnHierarchyChangeListener> mHierarchyChangeListeners =
+ new ObserverList<>();
+ private final ObserverList<OnSystemUiVisibilityChangeListener> mSystemUiChangeListeners =
+ new ObserverList<>();
+
+ /**
+ * The desired size of this view in {@link MeasureSpec}. Set by the host
+ * when it should be different from that of the parent.
+ */
+ private int mDesiredWidthMeasureSpec = DEFAULT_MEASURE_SPEC;
+ private int mDesiredHeightMeasureSpec = DEFAULT_MEASURE_SPEC;
+
+ private EventOffsetHandler mEventOffsetHandler;
+
+ /**
+ * Constructs a new ContentView for the appropriate Android version.
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param webContents The WebContents managing this content view.
+ * @return an instance of a ContentView.
+ */
+ public static ContentView createContentView(
+ Context context, EventOffsetHandler eventOffsetHandler) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return new ContentViewApi23(context, eventOffsetHandler);
+ }
+ return new ContentView(context, eventOffsetHandler);
+ }
+
+ /**
+ * Creates an instance of a ContentView.
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param webContents A pointer to the WebContents managing this content view.
+ */
+ ContentView(Context context, EventOffsetHandler eventOffsetHandler) {
+ super(context, null, android.R.attr.webViewStyle);
+
+ if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
+ setHorizontalScrollBarEnabled(false);
+ setVerticalScrollBarEnabled(false);
+ }
+
+ mEventOffsetHandler = eventOffsetHandler;
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ApiHelperForO.setDefaultFocusHighlightEnabled(this, false);
+ }
+
+ setOnHierarchyChangeListener(this);
+ setOnSystemUiVisibilityChangeListener(this);
+ }
+
+ protected WebContentsAccessibility getWebContentsAccessibility() {
+ return mWebContents != null && !mWebContents.isDestroyed()
+ ? WebContentsAccessibility.fromWebContents(mWebContents)
+ : null;
+ }
+
+ public void setWebContents(WebContents webContents) {
+ boolean wasFocused = isFocused();
+ boolean wasWindowFocused = hasWindowFocus();
+ boolean wasAttached = isAttachedToWindow();
+ if (wasFocused) onFocusChanged(false, View.FOCUS_FORWARD, null);
+ if (wasWindowFocused) onWindowFocusChanged(false);
+ if (wasAttached) onDetachedFromWindow();
+ mWebContents = webContents;
+ if (wasFocused) onFocusChanged(true, View.FOCUS_FORWARD, null);
+ if (wasWindowFocused) onWindowFocusChanged(true);
+ if (wasAttached) onAttachedToWindow();
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ WebContentsAccessibility wcax = getWebContentsAccessibility();
+ return wcax != null && wcax.supportsAction(action)
+ ? wcax.performAction(action, arguments)
+ : super.performAccessibilityAction(action, arguments);
+ }
+
+ /**
+ * Set the desired size of the view. The values are in {@link MeasureSpec}.
+ * @param width The width of the content view.
+ * @param height The height of the content view.
+ */
+ public void setDesiredMeasureSpec(int width, int height) {
+ mDesiredWidthMeasureSpec = width;
+ mDesiredHeightMeasureSpec = height;
+ }
+
+ @Override
+ public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
+ assert listener == this : "Use add/removeOnHierarchyChangeListener instead.";
+ super.setOnHierarchyChangeListener(listener);
+ }
+
+ /**
+ * Registers the given listener to receive state changes for the content view hierarchy.
+ * @param listener Listener to receive view hierarchy state changes.
+ */
+ public void addOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
+ mHierarchyChangeListeners.addObserver(listener);
+ }
+
+ /**
+ * Unregisters the given listener from receiving state changes for the content view hierarchy.
+ * @param listener Listener that doesn't want to receive view hierarchy state changes.
+ */
+ public void removeOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
+ mHierarchyChangeListeners.removeObserver(listener);
+ }
+
+ @Override
+ public void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener listener) {
+ assert listener == this : "Use add/removeOnSystemUiVisibilityChangeListener instead.";
+ super.setOnSystemUiVisibilityChangeListener(listener);
+ }
+
+ /**
+ * Registers the given listener to receive system UI visibility state changes.
+ * @param listener Listener to receive system UI visibility state changes.
+ */
+ public void addOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener listener) {
+ mSystemUiChangeListeners.addObserver(listener);
+ }
+
+ /**
+ * Unregisters the given listener from receiving system UI visibility state changes.
+ * @param listener Listener that doesn't want to receive state changes.
+ */
+ public void removeOnSystemUiVisibilityChangeListener(
+ OnSystemUiVisibilityChangeListener listener) {
+ mSystemUiChangeListeners.removeObserver(listener);
+ }
+
+ // View.OnHierarchyChangeListener implementation
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ for (OnHierarchyChangeListener listener : mHierarchyChangeListeners) {
+ listener.onChildViewRemoved(parent, child);
+ }
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ for (OnHierarchyChangeListener listener : mHierarchyChangeListeners) {
+ listener.onChildViewAdded(parent, child);
+ }
+ }
+
+ // View.OnHierarchyChangeListener implementation
+
+ @Override
+ public void onSystemUiVisibilityChange(int visibility) {
+ for (OnSystemUiVisibilityChangeListener listener : mSystemUiChangeListeners) {
+ listener.onSystemUiVisibilityChange(visibility);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mDesiredWidthMeasureSpec != DEFAULT_MEASURE_SPEC) {
+ widthMeasureSpec = mDesiredWidthMeasureSpec;
+ }
+ if (mDesiredHeightMeasureSpec != DEFAULT_MEASURE_SPEC) {
+ heightMeasureSpec = mDesiredHeightMeasureSpec;
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ WebContentsAccessibility wcax = getWebContentsAccessibility();
+ AccessibilityNodeProvider provider =
+ (wcax != null) ? wcax.getAccessibilityNodeProvider() : null;
+ return (provider != null) ? provider : super.getAccessibilityNodeProvider();
+ }
+
+ // Needed by ViewEventSink.InternalAccessDelegate
+ @Override
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ // Calls may come while/after WebContents is destroyed. See https://crbug.com/821750#c8.
+ if (mWebContents != null && mWebContents.isDestroyed()) return null;
+ return ImeAdapter.fromWebContents(mWebContents).onCreateInputConnection(outAttrs);
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor() {
+ if (mWebContents != null && mWebContents.isDestroyed()) return false;
+ return ImeAdapter.fromWebContents(mWebContents).onCheckIsTextEditor();
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ try {
+ TraceEvent.begin("ContentView.onFocusChanged");
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ if (mWebContents != null) {
+ getViewEventSink().setHideKeyboardOnBlur(true);
+ getViewEventSink().onViewFocusChanged(gainFocus);
+ }
+ } finally {
+ TraceEvent.end("ContentView.onFocusChanged");
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (mWebContents != null) {
+ getViewEventSink().onWindowFocusChanged(hasWindowFocus);
+ }
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return getEventForwarder().onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return isFocused() ? getEventForwarder().dispatchKeyEvent(event)
+ : super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ return getEventForwarder().onDragEvent(event, this);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent e) {
+ boolean ret = super.onInterceptTouchEvent(e);
+ mEventOffsetHandler.onInterceptTouchEvent(e);
+ return ret;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean ret = getEventForwarder().onTouchEvent(event);
+ mEventOffsetHandler.onTouchEvent(event);
+ return ret;
+ }
+
+ @Override
+ public boolean onInterceptHoverEvent(MotionEvent e) {
+ mEventOffsetHandler.onInterceptHoverEvent(e);
+ return super.onInterceptHoverEvent(e);
+ }
+
+ @Override
+ public boolean dispatchDragEvent(DragEvent e) {
+ mEventOffsetHandler.onPreDispatchDragEvent(e.getAction());
+ boolean ret = super.dispatchDragEvent(e);
+ mEventOffsetHandler.onPostDispatchDragEvent(e.getAction());
+ return ret;
+ }
+
+ /**
+ * Mouse move events are sent on hover enter, hover move and hover exit.
+ * They are sent on hover exit because sometimes it acts as both a hover
+ * move and hover exit.
+ */
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ boolean consumed = getEventForwarder().onHoverEvent(event);
+ WebContentsAccessibility wcax = getWebContentsAccessibility();
+ if (wcax != null && !wcax.isTouchExplorationEnabled()) super.onHoverEvent(event);
+ return consumed;
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return getEventForwarder().onGenericMotionEvent(event);
+ }
+
+ private EventForwarder getEventForwarder() {
+ return mWebContents != null ? mWebContents.getEventForwarder() : null;
+ }
+
+ private ViewEventSink getViewEventSink() {
+ return mWebContents != null ? ViewEventSink.from(mWebContents) : null;
+ }
+
+ @Override
+ public boolean performLongClick() {
+ return false;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (mWebContents != null) {
+ getViewEventSink().onConfigurationChanged(newConfig);
+ }
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Currently the ContentView scrolling happens in the native side. In
+ * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
+ * are overridden, so that View's mScrollX and mScrollY will be unchanged at
+ * (0, 0). This is critical for drawing ContentView correctly.
+ */
+ @Override
+ public void scrollBy(int x, int y) {
+ getEventForwarder().scrollBy(x, y);
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ getEventForwarder().scrollTo(x, y);
+ }
+
+ @Override
+ protected int computeHorizontalScrollExtent() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getLastFrameViewportWidthPixInt() : 0;
+ }
+
+ @Override
+ protected int computeHorizontalScrollOffset() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getScrollXPixInt() : 0;
+ }
+
+ @Override
+ protected int computeHorizontalScrollRange() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getContentWidthPixInt() : 0;
+ }
+
+ @Override
+ protected int computeVerticalScrollExtent() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getLastFrameViewportHeightPixInt() : 0;
+ }
+
+ @Override
+ protected int computeVerticalScrollOffset() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getScrollYPixInt() : 0;
+ }
+
+ @Override
+ protected int computeVerticalScrollRange() {
+ RenderCoordinates rc = getRenderCoordinates();
+ return rc != null ? rc.getContentHeightPixInt() : 0;
+ }
+
+ private RenderCoordinates getRenderCoordinates() {
+ return mWebContents != null ? RenderCoordinates.fromWebContents(mWebContents) : null;
+ }
+
+ // End FrameLayout overrides.
+
+ @Override
+ public boolean awakenScrollBars(int startDelay, boolean invalidate) {
+ // For the default implementation of ContentView which draws the scrollBars on the native
+ // side, calling this function may get us into a bad state where we keep drawing the
+ // scrollBars, so disable it by always returning false.
+ if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) return false;
+ return super.awakenScrollBars(startDelay, invalidate);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mWebContents != null) {
+ getViewEventSink().onAttachedToWindow();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mWebContents != null) {
+ getViewEventSink().onDetachedFromWindow();
+ }
+ }
+
+ // Implements SmartClipProvider
+ @Override
+ public void extractSmartClipData(int x, int y, int width, int height) {
+ if (mWebContents != null) {
+ mWebContents.requestSmartClipExtract(x, y, width, height);
+ }
+ }
+
+ // Implements SmartClipProvider
+ @Override
+ public void setSmartClipResultHandler(final Handler resultHandler) {
+ if (mWebContents != null) {
+ mWebContents.setSmartClipResultHandler(resultHandler);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Start Implementation of ViewEventSink.InternalAccessDelegate //
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public boolean super_onKeyUp(int keyCode, KeyEvent event) {
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean super_dispatchKeyEvent(KeyEvent event) {
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean super_onGenericMotionEvent(MotionEvent event) {
+ return super.onGenericMotionEvent(event);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // End Implementation of ViewEventSink.InternalAccessDelegate //
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static class ContentViewApi23 extends ContentView {
+ public ContentViewApi23(Context context, EventOffsetHandler eventOffsetHandler) {
+ super(context, eventOffsetHandler);
+ }
+
+ @Override
+ public void onProvideVirtualStructure(final ViewStructure structure) {
+ WebContentsAccessibility wcax = getWebContentsAccessibility();
+ if (wcax != null) wcax.onProvideVirtualStructure(structure, false);
+ }
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
new file mode 100644
index 00000000000..ca9f78ff358
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
@@ -0,0 +1,695 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.ValueCallback;
+import android.widget.FrameLayout;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.task.PostTask;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.resources.ResourceManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * This class manages the chromium compositor and the Surface that is used by
+ * the chromium compositor. Note it can be used to display only one WebContents.
+ * This allows switching between SurfaceView and TextureView as the source of
+ * the Surface used by chromium compositor, and attempts to make the switch
+ * visually seamless.
+ */
+@JNINamespace("weblayer")
+public class ContentViewRenderView extends FrameLayout {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MODE_SURFACE_VIEW, MODE_SURFACE_VIEW})
+ public @interface Mode {}
+ public static final int MODE_SURFACE_VIEW = 0;
+ public static final int MODE_TEXTURE_VIEW = 1;
+
+ // This is mode that is requested by client.
+ private SurfaceData mRequested;
+ // This is the mode that last supplied the Surface to the compositor.
+ // This should generally be equal to |mRequested| except during transitions.
+ private SurfaceData mCurrent;
+
+ // The native side of this object.
+ private long mNativeContentViewRenderView;
+
+ private WindowAndroid mWindowAndroid;
+ private WebContents mWebContents;
+
+ private int mBackgroundColor;
+ private int mWidth;
+ private int mHeight;
+
+ private int mWebContentsHeightDelta;
+
+ // Common interface to listen to surface related events.
+ private interface SurfaceEventListener {
+ void surfaceCreated();
+ void surfaceChanged(Surface surface, boolean canBeUsedWithSurfaceControl, int format,
+ int width, int height);
+ // |cacheBackBuffer| will delay destroying the EGLSurface until after the next swap.
+ void surfaceDestroyed(boolean cacheBackBuffer);
+ }
+
+ private final ArrayList<TrackedRunnable> mPendingRunnables = new ArrayList<>();
+
+ // Runnables posted via View.postOnAnimation may not run after the view is detached,
+ // if nothing else causes animation. However a pending runnable may held by a GC root
+ // from the thread itself, and thus can cause leaks. This class here is so ensure that
+ // on destroy, all pending tasks are run immediately so they do not lead to leaks.
+ private abstract class TrackedRunnable implements Runnable {
+ private boolean mHasRun;
+ public TrackedRunnable() {
+ mPendingRunnables.add(this);
+ }
+
+ @Override
+ public final void run() {
+ // View.removeCallbacks is not always reliable, and may run the callback even
+ // after it has been removed.
+ if (mHasRun) return;
+ assert mPendingRunnables.contains(this);
+ mPendingRunnables.remove(this);
+ mHasRun = true;
+ doRun();
+ }
+
+ protected abstract void doRun();
+ }
+
+ // Non-static implementation of SurfaceEventListener that forward calls to native Compositor.
+ // It is also responsible for updating |mRequested| and |mCurrent|.
+ private class SurfaceEventListenerImpl implements SurfaceEventListener {
+ private SurfaceData mSurfaceData;
+
+ public void setRequestData(SurfaceData surfaceData) {
+ assert mSurfaceData == null;
+ mSurfaceData = surfaceData;
+ }
+
+ @Override
+ public void surfaceCreated() {
+ assert mNativeContentViewRenderView != 0;
+ assert mSurfaceData == ContentViewRenderView.this.mRequested
+ || mSurfaceData == ContentViewRenderView.this.mCurrent;
+ if (ContentViewRenderView.this.mCurrent != null
+ && ContentViewRenderView.this.mCurrent != mSurfaceData) {
+ ContentViewRenderView.this.mCurrent.markForDestroy(true /* hasNextSurface */);
+ mSurfaceData.setSurfaceDataNeedsDestroy(ContentViewRenderView.this.mCurrent);
+ }
+ ContentViewRenderView.this.mCurrent = mSurfaceData;
+ ContentViewRenderViewJni.get().surfaceCreated(mNativeContentViewRenderView);
+ }
+
+ @Override
+ public void surfaceChanged(Surface surface, boolean canBeUsedWithSurfaceControl, int format,
+ int width, int height) {
+ assert mNativeContentViewRenderView != 0;
+ assert mSurfaceData == ContentViewRenderView.this.mCurrent;
+ ContentViewRenderViewJni.get().surfaceChanged(mNativeContentViewRenderView,
+ canBeUsedWithSurfaceControl, format, width, height, surface);
+ if (mWebContents != null) {
+ ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged(
+ mNativeContentViewRenderView, mWebContents, width, height);
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(boolean cacheBackBuffer) {
+ assert mNativeContentViewRenderView != 0;
+ assert mSurfaceData == ContentViewRenderView.this.mCurrent;
+ ContentViewRenderViewJni.get().surfaceDestroyed(
+ mNativeContentViewRenderView, cacheBackBuffer);
+ }
+ }
+
+ // Abstract differences between SurfaceView and TextureView behind this class.
+ // Also responsible for holding and calling callbacks.
+ private class SurfaceData implements SurfaceEventListener {
+ private class TextureViewWithInvalidate extends TextureView {
+ public TextureViewWithInvalidate(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void invalidate() {
+ // TextureView is invalidated when it receives a new frame from its SurfaceTexture.
+ // This is a safe place to indicate that this TextureView now has content and is
+ // ready to be shown.
+ super.invalidate();
+ destroyPreviousData();
+ }
+ }
+
+ @Mode
+ private final int mMode;
+ private final SurfaceEventListener mListener;
+ private final FrameLayout mParent;
+ private final Runnable mEvict;
+
+ private boolean mRanCallbacks;
+ private boolean mMarkedForDestroy;
+ private boolean mCachedSurfaceNeedsEviction;
+
+ private boolean mNeedsOnSurfaceDestroyed;
+
+ // During transitioning between two SurfaceData, there is a complicated series of calls to
+ // avoid visual artifacts.
+ // 1) Allocate new SurfaceData, and insert it into view hierarchy below the existing
+ // SurfaceData, so it is not yet showing.
+ // 2) When Surface is allocated by new View, swap chromium compositor to the
+ // new Surface. |markForDestroy| is called on the previous SurfaceData, and the two
+ // SurfaceDatas are linked through these two variables.
+ // Note at this point the existing view is still visible.
+ // 3) Wait until new SurfaceData decides that it has content and is ready to be shown
+ // * For TextureView, wait until TextureView.invalidate is called
+ // * For SurfaceView, wait for two swaps from the chromium compositor
+ // 4) New SurfaceData calls |destroy| on previous SurfaceData.
+ // * For TextureView, it is simply detached immediately from the view tree
+ // * For SurfaceView, to avoid flicker, move it to the back first before and wait
+ // two frames before detaching.
+ // 5) Previous SurfaceData runs callbacks on the new SurfaceData to signal the completion
+ // of the transition.
+ private SurfaceData mPrevSurfaceDataNeedsDestroy;
+ private SurfaceData mNextSurfaceDataNeedsRunCallback;
+
+ private final SurfaceHolderCallback mSurfaceCallback;
+ private final SurfaceView mSurfaceView;
+ private int mNumSurfaceViewSwapsUntilVisible;
+
+ private final TextureView mTextureView;
+ private final TextureViewSurfaceTextureListener mSurfaceTextureListener;
+
+ private final ArrayList<ValueCallback<Boolean>> mModeCallbacks = new ArrayList<>();
+
+ public SurfaceData(@Mode int mode, FrameLayout parent, SurfaceEventListener listener,
+ int backgroundColor, Runnable evict) {
+ mMode = mode;
+ mListener = listener;
+ mParent = parent;
+ mEvict = evict;
+ if (mode == MODE_SURFACE_VIEW) {
+ mSurfaceView = new SurfaceView(parent.getContext());
+ mSurfaceView.setZOrderMediaOverlay(true);
+ mSurfaceView.setBackgroundColor(backgroundColor);
+
+ mSurfaceCallback = new SurfaceHolderCallback(this);
+ mSurfaceView.getHolder().addCallback(mSurfaceCallback);
+ mSurfaceView.setVisibility(View.VISIBLE);
+
+ // TODO(boliu): This is only needed when video is lifted into a separate surface.
+ // Keeping this constantly will use one more byte per pixel constantly.
+ mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+ mTextureView = null;
+ mSurfaceTextureListener = null;
+ } else if (mode == MODE_TEXTURE_VIEW) {
+ mTextureView = new TextureViewWithInvalidate(parent.getContext());
+ mSurfaceTextureListener = new TextureViewSurfaceTextureListener(this);
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+ mTextureView.setVisibility(VISIBLE);
+
+ mSurfaceView = null;
+ mSurfaceCallback = null;
+ } else {
+ throw new RuntimeException("Illegal mode: " + mode);
+ }
+
+ // This postOnAnimation is to avoid manipulating the view tree inside layout or draw.
+ parent.postOnAnimation(new TrackedRunnable() {
+ @Override
+ protected void doRun() {
+ if (mMarkedForDestroy) return;
+ View view = (mMode == MODE_SURFACE_VIEW) ? mSurfaceView : mTextureView;
+ assert view != null;
+ // Always insert view for new surface below the existing view to avoid artifacts
+ // during surface swaps. Index 0 is the lowest child.
+ mParent.addView(view, 0,
+ new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ mParent.invalidate();
+ }
+ });
+ }
+
+ public void setSurfaceDataNeedsDestroy(SurfaceData surfaceData) {
+ assert !mMarkedForDestroy;
+ assert mPrevSurfaceDataNeedsDestroy == null;
+ mPrevSurfaceDataNeedsDestroy = surfaceData;
+ mPrevSurfaceDataNeedsDestroy.mNextSurfaceDataNeedsRunCallback = this;
+ }
+
+ public @Mode int getMode() {
+ return mMode;
+ }
+
+ public void addCallback(ValueCallback<Boolean> callback) {
+ assert !mMarkedForDestroy;
+ mModeCallbacks.add(callback);
+ if (mRanCallbacks) runCallbacks();
+ }
+
+ // Tearing down is separated into markForDestroy and destroy. After markForDestroy
+ // this class will is guaranteed to not issue any calls to its SurfaceEventListener.
+ public void markForDestroy(boolean hasNextSurface) {
+ if (mMarkedForDestroy) return;
+ mMarkedForDestroy = true;
+
+ if (mNeedsOnSurfaceDestroyed) {
+ // SurfaceView being used with SurfaceControl need to cache the back buffer
+ // (EGLSurface). Otherwise the surface is destroyed immediate before the
+ // SurfaceView is detached.
+ mCachedSurfaceNeedsEviction = hasNextSurface && mMode == MODE_SURFACE_VIEW;
+ mListener.surfaceDestroyed(mCachedSurfaceNeedsEviction);
+ mNeedsOnSurfaceDestroyed = false;
+ }
+
+ if (mMode == MODE_SURFACE_VIEW) {
+ mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
+ } else if (mMode == MODE_TEXTURE_VIEW) {
+ mTextureView.setSurfaceTextureListener(null);
+ } else {
+ assert false;
+ }
+ }
+
+ // Remove view from parent hierarchy.
+ public void destroy() {
+ assert mMarkedForDestroy;
+ runCallbacks();
+ // This postOnAnimation is to avoid manipulating the view tree inside layout or draw.
+ mParent.postOnAnimation(new TrackedRunnable() {
+ @Override
+ protected void doRun() {
+ if (mMode == MODE_SURFACE_VIEW) {
+ // Detaching a SurfaceView causes a flicker because the SurfaceView tears
+ // down the Surface in SurfaceFlinger before removing its hole in the view
+ // tree. This is a complicated heuristics to avoid this. It first moves the
+ // SurfaceView behind the new View. Then wait two frames before detaching
+ // the SurfaceView. Waiting for a single frame still causes flickers on
+ // high end devices like Pixel 3.
+ moveChildToBackWithoutDetach(mParent, mSurfaceView);
+ TrackedRunnable inner = new TrackedRunnable() {
+ @Override
+ public void doRun() {
+ mParent.removeView(mSurfaceView);
+ mParent.invalidate();
+ if (mCachedSurfaceNeedsEviction) {
+ mEvict.run();
+ mCachedSurfaceNeedsEviction = false;
+ }
+ runCallbackOnNextSurfaceData();
+ }
+ };
+ TrackedRunnable outer = new TrackedRunnable() {
+ @Override
+ public void doRun() {
+ mParent.postOnAnimation(inner);
+ }
+ };
+ mParent.postOnAnimation(outer);
+ } else if (mMode == MODE_TEXTURE_VIEW) {
+ mParent.removeView(mTextureView);
+ runCallbackOnNextSurfaceData();
+ } else {
+ assert false;
+ }
+ }
+ });
+ }
+
+ private void moveChildToBackWithoutDetach(ViewGroup parent, View child) {
+ final int numberOfChildren = parent.getChildCount();
+ final int childIndex = parent.indexOfChild(child);
+ if (childIndex <= 0) return;
+ for (int i = 0; i < childIndex; ++i) {
+ parent.bringChildToFront(parent.getChildAt(0));
+ }
+ assert parent.indexOfChild(child) == 0;
+ for (int i = 0; i < numberOfChildren - childIndex - 1; ++i) {
+ parent.bringChildToFront(parent.getChildAt(1));
+ }
+ parent.invalidate();
+ }
+
+ public void setBackgroundColor(int color) {
+ assert !mMarkedForDestroy;
+ if (mMode == MODE_SURFACE_VIEW) {
+ mSurfaceView.setBackgroundColor(color);
+ }
+ }
+
+ /** @return true if should keep swapping frames */
+ public boolean didSwapFrame() {
+ if (mSurfaceView != null && mSurfaceView.getBackground() != null) {
+ mSurfaceView.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSurfaceView != null) mSurfaceView.setBackgroundResource(0);
+ }
+ });
+ }
+ if (mMode == MODE_SURFACE_VIEW) {
+ // We have no reliable signal for when to show a SurfaceView. This is a heuristic
+ // (used by chrome as well) is to wait for 2 swaps from the chromium comopsitor
+ // as a signal that the SurfaceView has content and is ready to be displayed.
+ if (mNumSurfaceViewSwapsUntilVisible > 0) {
+ mNumSurfaceViewSwapsUntilVisible--;
+ }
+ if (mNumSurfaceViewSwapsUntilVisible == 0) {
+ destroyPreviousData();
+ }
+ return mNumSurfaceViewSwapsUntilVisible > 0;
+ }
+ return false;
+ }
+
+ private void destroyPreviousData() {
+ if (mPrevSurfaceDataNeedsDestroy != null) {
+ mPrevSurfaceDataNeedsDestroy.destroy();
+ mPrevSurfaceDataNeedsDestroy = null;
+ }
+ }
+
+ @Override
+ public void surfaceCreated() {
+ if (mMarkedForDestroy) return;
+
+ // On pre-M Android, layers start in the hidden state until a relayout happens.
+ // There is a bug that manifests itself when entering overlay mode on pre-M devices,
+ // where a relayout never happens. This bug is out of Chromium's control, but can be
+ // worked around by forcibly re-setting the visibility of the surface view.
+ // Otherwise, the screen stays black, and some tests fail.
+ if (mSurfaceView != null) {
+ mSurfaceView.setVisibility(mSurfaceView.getVisibility());
+ }
+ mListener.surfaceCreated();
+
+ if (!mRanCallbacks && mPrevSurfaceDataNeedsDestroy == null) {
+ runCallbacks();
+ }
+
+ mNeedsOnSurfaceDestroyed = true;
+ }
+
+ @Override
+ public void surfaceChanged(Surface surface, boolean canBeUsedWithSurfaceControl, int format,
+ int width, int height) {
+ if (mMarkedForDestroy) return;
+ mListener.surfaceChanged(surface, canBeUsedWithSurfaceControl, format, width, height);
+ mNumSurfaceViewSwapsUntilVisible = 2;
+ }
+
+ @Override
+ public void surfaceDestroyed(boolean cacheBackBuffer) {
+ if (mMarkedForDestroy) return;
+ assert mNeedsOnSurfaceDestroyed;
+ mListener.surfaceDestroyed(cacheBackBuffer);
+ mNeedsOnSurfaceDestroyed = false;
+ }
+
+ private void runCallbacks() {
+ mRanCallbacks = true;
+ if (mModeCallbacks.isEmpty()) return;
+ // PostTask to avoid possible reentrancy problems with embedder code.
+ PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+ ArrayList<ValueCallback<Boolean>> clone =
+ (ArrayList<ValueCallback<Boolean>>) mModeCallbacks.clone();
+ mModeCallbacks.clear();
+ for (ValueCallback<Boolean> run : clone) {
+ run.onReceiveValue(!mMarkedForDestroy);
+ }
+ });
+ }
+
+ private void runCallbackOnNextSurfaceData() {
+ if (mNextSurfaceDataNeedsRunCallback != null) {
+ mNextSurfaceDataNeedsRunCallback.runCallbacks();
+ mNextSurfaceDataNeedsRunCallback = null;
+ }
+ }
+ }
+
+ // Adapter for SurfaceHoolder.Callback.
+ private static class SurfaceHolderCallback implements SurfaceHolder.Callback {
+ private final SurfaceEventListener mListener;
+
+ public SurfaceHolderCallback(SurfaceEventListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mListener.surfaceCreated();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mListener.surfaceChanged(holder.getSurface(), true, format, width, height);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mListener.surfaceDestroyed(false /* cacheBackBuffer */);
+ }
+ }
+
+ // Adapter for TextureView.SurfaceTextureListener.
+ private static class TextureViewSurfaceTextureListener
+ implements TextureView.SurfaceTextureListener {
+ private final SurfaceEventListener mListener;
+
+ private SurfaceTexture mCurrentSurfaceTexture;
+ private Surface mCurrentSurface;
+
+ public TextureViewSurfaceTextureListener(SurfaceEventListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(
+ SurfaceTexture surfaceTexture, int width, int height) {
+ mListener.surfaceCreated();
+ onSurfaceTextureSizeChanged(surfaceTexture, width, height);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ mListener.surfaceDestroyed(false /* cacheBackBuffer */);
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(
+ SurfaceTexture surfaceTexture, int width, int height) {
+ if (mCurrentSurfaceTexture != surfaceTexture) {
+ mCurrentSurfaceTexture = surfaceTexture;
+ mCurrentSurface = new Surface(mCurrentSurfaceTexture);
+ }
+ mListener.surfaceChanged(mCurrentSurface, false, PixelFormat.OPAQUE, width, height);
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
+ }
+
+ /**
+ * Constructs a new ContentViewRenderView.
+ * This should be called and the {@link ContentViewRenderView} should be added to the view
+ * hierarchy before the first draw to avoid a black flash that is seen every time a
+ * {@link SurfaceView} is added.
+ * @param context The context used to create this.
+ */
+ public ContentViewRenderView(Context context) {
+ super(context);
+ setBackgroundColor(Color.WHITE);
+ }
+
+ /**
+ * Initialization that requires native libraries should be done here.
+ * Native code should add/remove the layers to be rendered through the ContentViewLayerRenderer.
+ * @param rootWindow The {@link WindowAndroid} this render view should be linked to.
+ */
+ public void onNativeLibraryLoaded(WindowAndroid rootWindow, @Mode int mode) {
+ assert rootWindow != null;
+ mNativeContentViewRenderView =
+ ContentViewRenderViewJni.get().init(ContentViewRenderView.this, rootWindow);
+ assert mNativeContentViewRenderView != 0;
+ mWindowAndroid = rootWindow;
+ requestMode(mode, (Boolean result) -> {});
+ }
+
+ public void requestMode(@Mode int mode, ValueCallback<Boolean> callback) {
+ assert mode == MODE_SURFACE_VIEW || mode == MODE_TEXTURE_VIEW;
+ assert callback != null;
+ if (mRequested != null && mRequested.getMode() != mode) {
+ if (mRequested != mCurrent) {
+ mRequested.markForDestroy(false /* hasNextSurface */);
+ mRequested.destroy();
+ }
+ mRequested = null;
+ }
+
+ if (mRequested == null) {
+ SurfaceEventListenerImpl listener = new SurfaceEventListenerImpl();
+ mRequested = new SurfaceData(
+ mode, this, listener, mBackgroundColor, this::evictCachedSurface);
+ listener.setRequestData(mRequested);
+ }
+ assert mRequested.getMode() == mode;
+ mRequested.addCallback(callback);
+ }
+
+ /**
+ * Sets how much to decrease the height of the WebContents by.
+ */
+ public void setWebContentsHeightDelta(int delta) {
+ if (delta == mWebContentsHeightDelta) return;
+ mWebContentsHeightDelta = delta;
+ updateWebContentsSize();
+ }
+
+ private void updateWebContentsSize() {
+ if (mWebContents == null) return;
+ mWebContents.setSize(mWidth, mHeight - mWebContentsHeightDelta);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ mWidth = w;
+ mHeight = h;
+ updateWebContentsSize();
+ }
+
+ /**
+ * View's method override to notify WindowAndroid about changes in its visibility.
+ */
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+
+ if (mWindowAndroid == null) return;
+
+ if (visibility == View.GONE) {
+ mWindowAndroid.onVisibilityChanged(false);
+ } else if (visibility == View.VISIBLE) {
+ mWindowAndroid.onVisibilityChanged(true);
+ }
+ }
+
+ /**
+ * Sets the background color of the surface / texture view. This method is necessary because
+ * the background color of ContentViewRenderView itself is covered by the background of
+ * SurfaceView.
+ * @param color The color of the background.
+ */
+ @Override
+ public void setBackgroundColor(int color) {
+ super.setBackgroundColor(color);
+ mBackgroundColor = color;
+ if (mRequested != null) {
+ mRequested.setBackgroundColor(color);
+ }
+ if (mCurrent != null) {
+ mCurrent.setBackgroundColor(color);
+ }
+ }
+
+ /**
+ * Should be called when the ContentViewRenderView is not needed anymore so its associated
+ * native resource can be freed.
+ */
+ public void destroy() {
+ if (mRequested != null) {
+ mRequested.markForDestroy(false /* hasNextSurface */);
+ mRequested.destroy();
+ if (mCurrent != null && mCurrent != mRequested) {
+ mCurrent.markForDestroy(false /* hasNextSurface */);
+ mCurrent.destroy();
+ }
+ }
+ mRequested = null;
+ mCurrent = null;
+
+ mWindowAndroid = null;
+
+ while (!mPendingRunnables.isEmpty()) {
+ TrackedRunnable runnable = mPendingRunnables.get(0);
+ removeCallbacks(runnable);
+ runnable.run();
+ assert !mPendingRunnables.contains(runnable);
+ }
+ ContentViewRenderViewJni.get().destroy(mNativeContentViewRenderView);
+ mNativeContentViewRenderView = 0;
+ }
+
+ public void setWebContents(WebContents webContents) {
+ assert mNativeContentViewRenderView != 0;
+ mWebContents = webContents;
+
+ if (webContents != null) {
+ updateWebContentsSize();
+ ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged(
+ mNativeContentViewRenderView, webContents, mWidth, mHeight);
+ }
+ ContentViewRenderViewJni.get().setCurrentWebContents(
+ mNativeContentViewRenderView, webContents);
+ }
+
+ public ResourceManager getResourceManager() {
+ return ContentViewRenderViewJni.get().getResourceManager(mNativeContentViewRenderView);
+ }
+
+ @CalledByNative
+ private boolean didSwapFrame() {
+ assert mCurrent != null;
+ return mCurrent.didSwapFrame();
+ }
+
+ private void evictCachedSurface() {
+ if (mNativeContentViewRenderView == 0) return;
+ ContentViewRenderViewJni.get().evictCachedSurface(mNativeContentViewRenderView);
+ }
+
+ public long getNativeHandle() {
+ return mNativeContentViewRenderView;
+ }
+
+ @NativeMethods
+ interface Natives {
+ long init(ContentViewRenderView caller, WindowAndroid rootWindow);
+ void destroy(long nativeContentViewRenderView);
+ void setCurrentWebContents(long nativeContentViewRenderView, WebContents webContents);
+ void onPhysicalBackingSizeChanged(
+ long nativeContentViewRenderView, WebContents webContents, int width, int height);
+ void surfaceCreated(long nativeContentViewRenderView);
+ void surfaceDestroyed(long nativeContentViewRenderView, boolean cacheBackBuffer);
+ void surfaceChanged(long nativeContentViewRenderView, boolean canBeUsedWithSurfaceControl,
+ int format, int width, int height, Surface surface);
+ void evictCachedSurface(long nativeContentViewRenderView);
+ ResourceManager getResourceManager(long nativeContentViewRenderView);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java
new file mode 100644
index 00000000000..ce39d8ef129
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java
@@ -0,0 +1,276 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.AndroidRuntimeException;
+
+import androidx.annotation.Nullable;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import org.chromium.base.Log;
+import org.chromium.base.PathUtils;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.components.crash.browser.ChildProcessCrashObserver;
+import org.chromium.components.minidump_uploader.CrashFileManager;
+import org.chromium.weblayer_private.interfaces.ICrashReporterController;
+import org.chromium.weblayer_private.interfaces.ICrashReporterControllerClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Provides the implementation of the API for managing captured crash reports.
+ *
+ * @see org.chromium.weblayer.CrashReporterController
+ */
+public final class CrashReporterControllerImpl extends ICrashReporterController.Stub {
+ private static final String TAG = "CrashReporter";
+ private static final int MAX_UPLOAD_RETRIES = 3;
+
+ @Nullable
+ private ICrashReporterControllerClient mClient;
+ private CrashFileManager mCrashFileManager;
+ private boolean mIsNativeInitialized;
+
+ private static class Holder {
+ static CrashReporterControllerImpl sInstance = new CrashReporterControllerImpl();
+ }
+
+ private CrashReporterControllerImpl() {
+ ChildProcessCrashObserver.registerCrashCallback(
+ new ChildProcessCrashObserver.ChildCrashedCallback() {
+ @Override
+ public void childCrashed(int pid) {
+ processNewMinidumps();
+ }
+ });
+ }
+
+ public static CrashReporterControllerImpl getInstance(IObjectWrapper appContextWrapper) {
+ // This is a no-op if init has already happened.
+ WebLayerImpl.minimalInitForContext(appContextWrapper);
+ return Holder.sInstance;
+ }
+
+ public void notifyNativeInitialized() {
+ mIsNativeInitialized = true;
+ if (mClient != null) {
+ processNewMinidumps();
+ }
+ }
+
+ @Override
+ public void deleteCrash(String localId) {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ deleteCrashOnBackgroundThread(localId);
+ try {
+ mClient.onCrashDeleted(localId);
+ } catch (RemoteException e) {
+ throw new AndroidRuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void uploadCrash(String localId) {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ File minidumpFile = getCrashFileManager().getCrashFileWithLocalId(localId);
+ MinidumpUploader.Result result = new MinidumpUploader().upload(minidumpFile);
+ if (result.mSuccess) {
+ CrashFileManager.markUploadSuccess(minidumpFile);
+ } else {
+ CrashFileManager.tryIncrementAttemptNumber(minidumpFile);
+ }
+ try {
+ if (result.mSuccess) {
+ mClient.onCrashUploadSucceeded(localId, result.mResult);
+ } else {
+ mClient.onCrashUploadFailed(localId, result.mResult);
+ }
+ } catch (RemoteException e) {
+ throw new AndroidRuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public @Nullable Bundle getCrashKeys(String localId) {
+ JSONObject data = readSidecar(localId);
+ if (data == null) {
+ return null;
+ }
+ Bundle result = new Bundle();
+ Iterator<String> iter = data.keys();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ try {
+ result.putCharSequence(key, data.getString(key));
+ } catch (JSONException e) {
+ // Skip non-string values.
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void checkForPendingCrashReports() {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ try {
+ mClient.onPendingCrashReports(getPendingMinidumpsOnBackgroundThread());
+ } catch (RemoteException e) {
+ throw new AndroidRuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void setClient(ICrashReporterControllerClient client) {
+ mClient = client;
+ if (mIsNativeInitialized) {
+ processNewMinidumps();
+ }
+ }
+
+ /** Start an async task to import crashes, and notify if any are found. */
+ private void processNewMinidumps() {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ String[] localIds = processNewMinidumpsOnBackgroundThread();
+ if (localIds.length > 0) {
+ try {
+ mClient.onPendingCrashReports(localIds);
+ } catch (RemoteException e) {
+ throw new AndroidRuntimeException(e);
+ }
+ }
+ });
+ }
+
+ /** Delete a crash report (and any sidecar file) given its local ID. */
+ private void deleteCrashOnBackgroundThread(String localId) {
+ File minidumpFile = getCrashFileManager().getCrashFileWithLocalId(localId);
+ File sidecarFile = sidecarFile(localId);
+ if (minidumpFile != null) {
+ CrashFileManager.deleteFile(minidumpFile);
+ }
+ if (sidecarFile != null) {
+ CrashFileManager.deleteFile(sidecarFile);
+ }
+ }
+
+ /**
+ * Determine the set of crashes that are currently ready to be uploaded.
+ *
+ * Clean out crashes that are too old, and return the any remaining crashes that have not
+ * exceeded their upload retry limit.
+ *
+ * @return An array of local IDs for crashes that are ready to be uploaded.
+ */
+ private String[] getPendingMinidumpsOnBackgroundThread() {
+ getCrashFileManager().cleanOutAllNonFreshMinidumpFiles();
+ File[] pendingMinidumps =
+ getCrashFileManager().getMinidumpsReadyForUpload(MAX_UPLOAD_RETRIES);
+ ArrayList<String> localIds = new ArrayList<>(pendingMinidumps.length);
+ for (File minidump : pendingMinidumps) {
+ localIds.add(CrashFileManager.getCrashLocalIdFromFileName(minidump.getName()));
+ }
+ return localIds.toArray(new String[0]);
+ }
+
+ /**
+ * Use the CrashFileManager to import crashes from crashpad.
+ *
+ * For each imported crash, a sidecar file (in JSON format) is written, containing the
+ * crash keys that were recorded at the time of the crash.
+ *
+ * @return An array of local IDs of the new crashes (may be empty).
+ */
+ private String[] processNewMinidumpsOnBackgroundThread() {
+ Map<String, Map<String, String>> crashesInfoMap =
+ getCrashFileManager().importMinidumpsCrashKeys();
+ ArrayList<String> localIds = new ArrayList<>(crashesInfoMap.size());
+ for (Map.Entry<String, Map<String, String>> entry : crashesInfoMap.entrySet()) {
+ JSONObject crashKeysJson = new JSONObject(entry.getValue());
+ String uuid = entry.getKey();
+ // TODO(tobiasjs): the minidump uploader uses the last component of the uuid as
+ // the local ID. The ergonomics of this should be improved.
+ localIds.add(CrashFileManager.getCrashLocalIdFromFileName(uuid + ".dmp"));
+ writeSidecar(uuid, crashKeysJson);
+ }
+ for (File minidump : getCrashFileManager().getMinidumpsSansLogcat()) {
+ CrashFileManager.trySetReadyForUpload(minidump);
+ }
+ return localIds.toArray(new String[0]);
+ }
+
+ /**
+ * Generate a sidecar file path given a crash local ID.
+ *
+ * The sidecar file holds a JSON representation of the crash keys associated
+ * with the crash. All crash keys and values are strings.
+ */
+ private @Nullable File sidecarFile(String localId) {
+ File minidumpFile = getCrashFileManager().getCrashFileWithLocalId(localId);
+ if (minidumpFile == null) {
+ return null;
+ }
+ String uuid = minidumpFile.getName().split("\\.")[0];
+ return new File(minidumpFile.getParent(), uuid + ".json");
+ }
+
+ /** Write JSON formatted crash key data to the sidecar file for a crash. */
+ private void writeSidecar(String localId, JSONObject data) {
+ File sidecar = sidecarFile(localId);
+ if (sidecar == null) {
+ return;
+ }
+ try (FileOutputStream out = new FileOutputStream(sidecar)) {
+ out.write(data.toString().getBytes("UTF-8"));
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write crash keys JSON for crash " + localId);
+ sidecar.delete();
+ }
+ }
+
+ /** Read JSON formatted crash key data previously written to a crash sidecar file. */
+ private @Nullable JSONObject readSidecar(String localId) {
+ File sidecar = sidecarFile(localId);
+ if (sidecar == null) {
+ return null;
+ }
+ try (FileInputStream in = new FileInputStream(sidecar)) {
+ byte[] data = new byte[(int) sidecar.length()];
+ int offset = 0;
+ while (offset < data.length) {
+ int count = in.read(data, offset, data.length - offset);
+ if (count <= 0) break;
+ offset += count;
+ }
+ return new JSONObject(new String(data, "UTF-8"));
+ } catch (IOException | JSONException e) {
+ return null;
+ }
+ }
+
+ private CrashFileManager getCrashFileManager() {
+ if (mCrashFileManager == null) {
+ File cacheDir = new File(PathUtils.getCacheDirectory());
+ // Make sure the cache dir has been created, since this may be called before WebLayer
+ // has been initialized.
+ cacheDir.mkdir();
+ mCrashFileManager = new CrashFileManager(cacheDir);
+ }
+ return mCrashFileManager;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java
new file mode 100644
index 00000000000..ad33b663a44
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
+
+/**
+ * Owns the c++ DownloadCallbackProxy class, which is responsible for forwarding all
+ * DownloadDelegate delegate calls to this class, which in turn forwards to the
+ * DownloadCallbackClient.
+ */
+@JNINamespace("weblayer")
+public final class DownloadCallbackProxy {
+ private long mNativeDownloadCallbackProxy;
+ private IDownloadCallbackClient mClient;
+
+ DownloadCallbackProxy(long tab, IDownloadCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ mNativeDownloadCallbackProxy =
+ DownloadCallbackProxyJni.get().createDownloadCallbackProxy(this, tab);
+ }
+
+ public void setClient(IDownloadCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ }
+
+ public void destroy() {
+ DownloadCallbackProxyJni.get().deleteDownloadCallbackProxy(mNativeDownloadCallbackProxy);
+ mNativeDownloadCallbackProxy = 0;
+ }
+
+ @CalledByNative
+ private boolean interceptDownload(String url, String userAgent, String contentDisposition,
+ String mimetype, long contentLength) throws RemoteException {
+ return mClient.interceptDownload(
+ url, userAgent, contentDisposition, mimetype, contentLength);
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createDownloadCallbackProxy(DownloadCallbackProxy proxy, long tab);
+ void deleteDownloadCallbackProxy(long proxy);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java
new file mode 100644
index 00000000000..639a2003568
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
+
+/**
+ * Owns the c++ ErrorPageCallbackProxy class, which is responsible for forwarding all
+ * ErrorPageDelegate calls to this class, which in turn forwards to the
+ * ErrorPageCallbackClient.
+ */
+@JNINamespace("weblayer")
+public final class ErrorPageCallbackProxy {
+ private long mNativeErrorPageCallbackProxy;
+ private IErrorPageCallbackClient mClient;
+
+ ErrorPageCallbackProxy(long tab, IErrorPageCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ mNativeErrorPageCallbackProxy =
+ ErrorPageCallbackProxyJni.get().createErrorPageCallbackProxy(this, tab);
+ }
+
+ public void setClient(IErrorPageCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ }
+
+ public void destroy() {
+ ErrorPageCallbackProxyJni.get().deleteErrorPageCallbackProxy(mNativeErrorPageCallbackProxy);
+ mNativeErrorPageCallbackProxy = 0;
+ }
+
+ @CalledByNative
+ private boolean onBackToSafety() throws RemoteException {
+ return mClient.onBackToSafety();
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createErrorPageCallbackProxy(ErrorPageCallbackProxy proxy, long tab);
+ void deleteErrorPageCallbackProxy(long proxy);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationHandler.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationHandler.java
new file mode 100644
index 00000000000..dc976a683e2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationHandler.java
@@ -0,0 +1,99 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Browser;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class that handles navigations that should be transformed to intents. Logic taken primarly from
+ * //android_webview's AwContentsClient.java:sendBrowsingIntent(), with some additional logic
+ * from //android_webview's WebViewBrowserActivity.java:startBrowsingIntent().
+ */
+@JNINamespace("weblayer")
+public class ExternalNavigationHandler {
+ private static final String TAG = "ExternalNavHandler";
+
+ static final Pattern BROWSER_URI_SCHEMA =
+ Pattern.compile("(?i)" // switch on case insensitive matching
+ + "(" // begin group for schema
+ + "(?:http|https|file)://"
+ + "|(?:inline|data|about|chrome|javascript):"
+ + ")"
+ + ".*");
+
+ @CalledByNative
+ private static boolean shouldOverrideUrlLoading(
+ String url, boolean hasUserGesture, boolean isRedirect, boolean isMainFrame) {
+ // Check for regular URIs that WebLayer supports by itself.
+ // TODO(blundell): Port over WebViewBrowserActivity's
+ // isSpecializedHandlerAvailable() check that checks whether there's an app for handling
+ // the scheme?
+ Matcher m = BROWSER_URI_SCHEMA.matcher(url);
+ if (m.matches()) {
+ return false;
+ }
+
+ if (!hasUserGesture && !isRedirect) {
+ Log.w(TAG, "Denied starting an intent without a user gesture, URI %s", url);
+ return true;
+ }
+
+ Intent intent;
+ // Perform generic parsing of the URI to turn it into an Intent.
+ try {
+ intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
+ } catch (Exception ex) {
+ Log.w(TAG, "Bad URI %s", url, ex);
+ return false;
+ }
+ // Sanitize the Intent, ensuring web pages can not bypass browser
+ // security (only access to BROWSABLE activities).
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+
+ // Ensure that startActivity() succeeds even if the application context
+ // isn't an Activity. This also matches Chrome's behavior (see
+ // //chrome's ExternalNavigationHandler.java:PrepareExternalIntent()).
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ intent.setComponent(null);
+ Intent selector = intent.getSelector();
+ if (selector != null) {
+ selector.addCategory(Intent.CATEGORY_BROWSABLE);
+ selector.setComponent(null);
+ }
+
+ Context context = ContextUtils.getApplicationContext();
+
+ // 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, context.getPackageName());
+
+ try {
+ context.startActivity(intent);
+ return true;
+ } catch (ActivityNotFoundException ex) {
+ Log.w(TAG, "No application can handle %s", url);
+ } catch (SecurityException ex) {
+ // This can happen if the Activity is exported="true", guarded by a permission, and sets
+ // up an intent filter matching this intent. This is a valid configuration for an
+ // Activity, so instead of crashing, we catch the exception and do nothing. See
+ // https://crbug.com/808494 and https://crbug.com/889300.
+ Log.w(TAG, "SecurityException when starting intent for %s", url);
+ }
+
+ return false;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java
new file mode 100644
index 00000000000..bf19c8e96b2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import org.chromium.base.compat.ApiHelperForM;
+import org.chromium.ui.base.AndroidPermissionDelegateWithRequester;
+
+/**
+ * AndroidPermissionDelegate implementation for BrowserFragment.
+ */
+public class FragmentAndroidPermissionDelegate extends AndroidPermissionDelegateWithRequester {
+ private BrowserFragmentImpl mFragment;
+
+ public FragmentAndroidPermissionDelegate(BrowserFragmentImpl fragment) {
+ mFragment = fragment;
+ }
+
+ @Override
+ protected final boolean shouldShowRequestPermissionRationale(String permission) {
+ if (mFragment.getActivity() == null) return false;
+ return mFragment.shouldShowRequestPermissionRationale(permission);
+ }
+
+ @Override
+ protected final boolean isPermissionRevokedByPolicyInternal(String permission) {
+ if (mFragment.getActivity() == null) return false;
+ return ApiHelperForM.isPermissionRevokedByPolicy(mFragment.getActivity(), permission);
+ }
+
+ @Override
+ protected final boolean requestPermissionsFromRequester(String[] permissions, int requestCode) {
+ if (mFragment.getActivity() == null) return false;
+ mFragment.requestPermissions(permissions, requestCode);
+ return true;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java
new file mode 100644
index 00000000000..7b6ba3392c2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FragmentWindowAndroid.java
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+
+import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
+import org.chromium.ui.base.IntentWindowAndroid;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Implements intent sending for a fragment based window. This should be created when
+ * onAttach() is called on the fragment, and destroyed when onDetach() is called.
+ */
+public class FragmentWindowAndroid extends IntentWindowAndroid {
+ private BrowserFragmentImpl mFragment;
+
+ FragmentWindowAndroid(Context context, BrowserFragmentImpl fragment) {
+ super(context);
+ mFragment = fragment;
+
+ setKeyboardDelegate(new ActivityKeyboardVisibilityDelegate(getActivity()));
+ setAndroidPermissionDelegate(new FragmentAndroidPermissionDelegate(mFragment));
+ }
+
+ @Override
+ protected final boolean startIntentSenderForResult(IntentSender intentSender, int requestCode) {
+ return mFragment.startIntentSenderForResult(
+ intentSender, requestCode, new Intent(), 0, 0, 0, null);
+ }
+
+ @Override
+ protected final boolean startActivityForResult(Intent intent, int requestCode) {
+ return mFragment.startActivityForResult(intent, requestCode, null);
+ }
+
+ @Override
+ public final WeakReference<Activity> getActivity() {
+ return new WeakReference<>(mFragment.getActivity());
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java
new file mode 100644
index 00000000000..fb6b8a31154
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+import android.webkit.ValueCallback;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+/**
+ * Owns the c++ FullscreenCallbackProxy class, which is responsible for forwarding all
+ * FullscreenDelegate delegate calls to this class, which in turn forwards to the
+ * FullscreenCallbackClient.
+ */
+@JNINamespace("weblayer")
+public final class FullscreenCallbackProxy {
+ private long mNativeFullscreenCallbackProxy;
+ private IFullscreenCallbackClient mClient;
+
+ FullscreenCallbackProxy(long tab, IFullscreenCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ mNativeFullscreenCallbackProxy =
+ FullscreenCallbackProxyJni.get().createFullscreenCallbackProxy(this, tab);
+ }
+
+ public void setClient(IFullscreenCallbackClient client) {
+ assert client != null;
+ mClient = client;
+ }
+
+ public void destroy() {
+ FullscreenCallbackProxyJni.get().deleteFullscreenCallbackProxy(
+ mNativeFullscreenCallbackProxy);
+ mNativeFullscreenCallbackProxy = 0;
+ }
+
+ @CalledByNative
+ private void enterFullscreen() throws RemoteException {
+ ValueCallback<Void> exitFullscreenCallback = new ValueCallback<Void>() {
+ @Override
+ public void onReceiveValue(Void result) {
+ if (mNativeFullscreenCallbackProxy == 0) {
+ throw new IllegalStateException("Called after destroy()");
+ }
+ FullscreenCallbackProxyJni.get().doExitFullscreen(
+ mNativeFullscreenCallbackProxy, FullscreenCallbackProxy.this);
+ }
+ };
+ mClient.enterFullscreen(ObjectWrapper.wrap(exitFullscreenCallback));
+ }
+
+ @CalledByNative
+ private void exitFullscreen() throws RemoteException {
+ mClient.exitFullscreen();
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createFullscreenCallbackProxy(FullscreenCallbackProxy proxy, long tab);
+ void deleteFullscreenCallbackProxy(long proxy);
+ void doExitFullscreen(long nativeFullscreenCallbackProxy, FullscreenCallbackProxy proxy);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridge.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridge.java
new file mode 100644
index 00000000000..21b5a59c405
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridge.java
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * This class manages functionality related to Google Mobile Services (i.e. GMS).
+ * Platform specific implementations are provided in GmsBridgeImpl.java.
+ */
+public abstract class GmsBridge {
+ private static GmsBridge sInstance;
+ private static final Object sInstanceLock = new Object();
+
+ private static HandlerThread sHandlerThread;
+ private static Handler sHandler;
+ private static final Object sHandlerLock = new Object();
+
+ protected GmsBridge() {}
+
+ public static GmsBridge getInstance() {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ // Load an instance of GmsBridgeImpl. Because this can change depending on
+ // the GN configuration, this may not be the GmsBridgeImpl defined upstream.
+ sInstance = new GmsBridgeImpl();
+ }
+ return sInstance;
+ }
+ }
+
+ // Return a handler appropriate for executing blocking Platform Service tasks.
+ public static Handler getHandler() {
+ synchronized (sHandlerLock) {
+ if (sHandler == null) {
+ sHandlerThread = new HandlerThread("GmsBridgeHandlerThread");
+ sHandlerThread.start();
+ sHandler = new Handler(sHandlerThread.getLooper());
+ }
+ }
+ return sHandler;
+ }
+
+ // Returns true if the WebLayer can use Google Mobile Services (GMS).
+ public boolean canUseGms() {
+ return false;
+ }
+
+ public void setSafeBrowsingHandler() {
+ // We don't have this specialized service here.
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridgeImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridgeImpl.java
new file mode 100644
index 00000000000..315f34a90be
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GmsBridgeImpl.java
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+/**
+ * Instantiable version of {@link GmsBridge}, don't add anything to this class!
+ * Downstream targets may provide a different implementation.
+ */
+public class GmsBridgeImpl extends GmsBridge {}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MinidumpUploader.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MinidumpUploader.java
new file mode 100644
index 00000000000..f84ccfa462a
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MinidumpUploader.java
@@ -0,0 +1,188 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import org.chromium.components.minidump_uploader.util.HttpURLConnectionFactory;
+import org.chromium.components.minidump_uploader.util.HttpURLConnectionFactoryImpl;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * This class tries to upload a minidump to the crash server.
+ *
+ * Minidumps are stored in multipart MIME format ready to form the body of a POST request. The MIME
+ * boundary forms the first line of the file.
+ */
+public class MinidumpUploader {
+ // TODO(crbug.com/1029724) unfork this class back to //components/minidump_uploader
+ private static final String CRASH_URL_STRING = "https://clients2.google.com/cr/report";
+ private static final String CONTENT_TYPE_TMPL = "multipart/form-data; boundary=%s";
+
+ private final HttpURLConnectionFactory mHttpURLConnectionFactory;
+
+ /* package */ static final class Result {
+ final boolean mSuccess;
+ final int mStatus;
+ final String mResult;
+
+ private Result(boolean success, int status, String result) {
+ mSuccess = success;
+ mStatus = status;
+ mResult = result;
+ }
+
+ static Result failure(String result) {
+ return new Result(false, -1, result);
+ }
+
+ static Result failure(int status, String result) {
+ return new Result(false, status, result);
+ }
+
+ static Result success(String result) {
+ return new Result(true, 0, result);
+ }
+ }
+
+ public MinidumpUploader() {
+ this(new HttpURLConnectionFactoryImpl());
+ }
+
+ public MinidumpUploader(HttpURLConnectionFactory httpURLConnectionFactory) {
+ mHttpURLConnectionFactory = httpURLConnectionFactory;
+ }
+
+ public Result upload(File fileToUpload) {
+ try {
+ if (fileToUpload == null || !fileToUpload.exists()) {
+ return Result.failure("Crash report does not exist");
+ }
+ HttpURLConnection connection =
+ mHttpURLConnectionFactory.createHttpURLConnection(CRASH_URL_STRING);
+ if (connection == null) {
+ return Result.failure("Failed to create connection");
+ }
+ configureConnectionForHttpPost(connection, readBoundary(fileToUpload));
+
+ try (InputStream minidumpInputStream = new FileInputStream(fileToUpload);
+ OutputStream requestBodyStream =
+ new GZIPOutputStream(connection.getOutputStream())) {
+ streamCopy(minidumpInputStream, requestBodyStream);
+ int responseCode = connection.getResponseCode();
+ if (isSuccessful(responseCode)) {
+ // The crash server returns the crash ID in the response body.
+ String responseContent = getResponseContentAsString(connection);
+ String uploadId = responseContent != null ? responseContent : "unknown";
+ return Result.success(uploadId);
+ } else {
+ // Return the remote error code and message.
+ return Result.failure(responseCode, connection.getResponseMessage());
+ }
+ } finally {
+ connection.disconnect();
+ }
+ } catch (IOException | RuntimeException e) {
+ return Result.failure(e.getMessage());
+ }
+ }
+
+ /**
+ * Configures a HttpURLConnection to send a HTTP POST request for uploading the minidump.
+ *
+ * This also reads the content-type from the minidump file.
+ *
+ * @param connection the HttpURLConnection to configure
+ * @param boundary the MIME boundary used in the request body
+ */
+ private void configureConnectionForHttpPost(HttpURLConnection connection, String boundary) {
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Connection", "Keep-Alive");
+ connection.setRequestProperty("Content-Encoding", "gzip");
+ connection.setRequestProperty("Content-Type", String.format(CONTENT_TYPE_TMPL, boundary));
+ }
+
+ /**
+ * Get the boundary from the file, we need it for the content-type.
+ *
+ * @return the boundary if found, else null.
+ * @throws IOException
+ */
+ private String readBoundary(File fileToUpload) throws IOException {
+ try (FileReader fileReader = new FileReader(fileToUpload);
+ BufferedReader reader = new BufferedReader(fileReader)) {
+ String boundary = reader.readLine();
+ if (boundary == null || boundary.trim().isEmpty()) {
+ throw new RuntimeException(fileToUpload + " does not have a MIME boundary");
+ }
+ boundary = boundary.trim();
+ if (!boundary.startsWith("--") || boundary.length() < 10) {
+ throw new RuntimeException(fileToUpload + " does not have a MIME boundary");
+ }
+ // Note: The regex allows all alphanumeric characters, as well as dashes.
+ // This matches the code that generates minidumps boundaries:
+ // https://chromium.googlesource.com/crashpad/crashpad/+/0c322ecc3f711c34fbf85b2cbe69f38b8dbccf05/util/net/http_multipart_builder.cc#36
+ if (!boundary.matches("^[a-zA-Z0-9-]*$")) {
+ throw new RuntimeException(
+ fileToUpload.getName() + " has an illegal MIME boundary: " + boundary);
+ }
+ boundary = boundary.substring(2); // Remove the initial --
+ return boundary;
+ }
+ }
+
+ /**
+ * Returns whether the response code indicates a successful HTTP request.
+ *
+ * @param responseCode the response code
+ * @return true if response code indicates success, false otherwise.
+ */
+ private boolean isSuccessful(int responseCode) {
+ return responseCode == 200 || responseCode == 201 || responseCode == 202;
+ }
+
+ /**
+ * Reads the response from |connection| as a String.
+ *
+ * @param connection the connection to read the response from.
+ * @return the content of the response.
+ * @throws IOException
+ */
+ private String getResponseContentAsString(HttpURLConnection connection) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ streamCopy(connection.getInputStream(), baos);
+ if (baos.size() == 0) {
+ return null;
+ }
+ return baos.toString();
+ }
+
+ /**
+ * Copies all available data from |inStream| to |outStream|. Closes both
+ * streams when done.
+ *
+ * @param inStream the stream to read
+ * @param outStream the stream to write to
+ * @throws IOException
+ */
+ private void streamCopy(InputStream inStream, OutputStream outStream) throws IOException {
+ byte[] temp = new byte[4096];
+ int bytesRead = inStream.read(temp);
+ while (bytesRead >= 0) {
+ outStream.write(temp, 0, bytesRead);
+ bytesRead = inStream.read(temp);
+ }
+ inStream.close();
+ outStream.close();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
index d9161e3374c..3fb45d58fb1 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
@@ -5,72 +5,89 @@
package org.chromium.weblayer_private;
import android.os.RemoteException;
-import android.util.AndroidRuntimeException;
-import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
-import org.chromium.weblayer_private.aidl.INavigationController;
-import org.chromium.weblayer_private.aidl.INavigationControllerClient;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.INavigationController;
+import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
/**
* Acts as the bridge between java and the C++ implementation of of NavigationController.
*/
@JNINamespace("weblayer")
public final class NavigationControllerImpl extends INavigationController.Stub {
- private static final String TAG = "WebLayer";
-
private long mNativeNavigationController;
- private BrowserControllerImpl mBrowserController;
+ private TabImpl mTab;
private INavigationControllerClient mNavigationControllerClient;
- public NavigationControllerImpl(
- BrowserControllerImpl browserController, INavigationControllerClient client) {
+ public NavigationControllerImpl(TabImpl tab, INavigationControllerClient client) {
mNavigationControllerClient = client;
- mBrowserController = browserController;
+ mTab = tab;
mNativeNavigationController =
- nativeGetNavigationController(browserController.getNativeBrowserController());
- nativeSetNavigationControllerImpl(mNativeNavigationController);
+ NavigationControllerImplJni.get().getNavigationController(tab.getNativeTab());
+ NavigationControllerImplJni.get().setNavigationControllerImpl(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public void navigate(String uri) {
- nativeNavigate(mNativeNavigationController, uri);
+ NavigationControllerImplJni.get().navigate(
+ mNativeNavigationController, NavigationControllerImpl.this, uri);
}
@Override
public void goBack() {
- nativeGoBack(mNativeNavigationController);
+ NavigationControllerImplJni.get().goBack(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public void goForward() {
- nativeGoForward(mNativeNavigationController);
+ NavigationControllerImplJni.get().goForward(
+ mNativeNavigationController, NavigationControllerImpl.this);
+ }
+
+ @Override
+ public boolean canGoBack() {
+ return NavigationControllerImplJni.get().canGoBack(
+ mNativeNavigationController, NavigationControllerImpl.this);
+ }
+
+ @Override
+ public boolean canGoForward() {
+ return NavigationControllerImplJni.get().canGoForward(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public void reload() {
- nativeReload(mNativeNavigationController);
+ NavigationControllerImplJni.get().reload(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public void stop() {
- nativeStop(mNativeNavigationController);
+ NavigationControllerImplJni.get().stop(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public int getNavigationListSize() {
- return nativeGetNavigationListSize(mNativeNavigationController);
+ return NavigationControllerImplJni.get().getNavigationListSize(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public int getNavigationListCurrentIndex() {
- return nativeGetNavigationListCurrentIndex(mNativeNavigationController);
+ return NavigationControllerImplJni.get().getNavigationListCurrentIndex(
+ mNativeNavigationController, NavigationControllerImpl.this);
}
@Override
public String getNavigationEntryDisplayUri(int index) {
- return nativeGetNavigationEntryDisplayUri(mNativeNavigationController, index);
+ return NavigationControllerImplJni.get().getNavigationEntryDisplayUri(
+ mNativeNavigationController, NavigationControllerImpl.this, index);
}
@CalledByNative
@@ -79,65 +96,64 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
}
@CalledByNative
- private void navigationStarted(NavigationImpl navigation) {
- try {
- mNavigationControllerClient.navigationStarted(navigation.getClientNavigation());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call navigationStarted.", e);
- throw new AndroidRuntimeException(e);
- }
+ private void navigationStarted(NavigationImpl navigation) throws RemoteException {
+ mNavigationControllerClient.navigationStarted(navigation.getClientNavigation());
+ }
+
+ @CalledByNative
+ private void navigationRedirected(NavigationImpl navigation) throws RemoteException {
+ mNavigationControllerClient.navigationRedirected(navigation.getClientNavigation());
}
@CalledByNative
- private void navigationRedirected(NavigationImpl navigation) {
- try {
- mNavigationControllerClient.navigationRedirected(navigation.getClientNavigation());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call navigationRedirected.", e);
- throw new AndroidRuntimeException(e);
- }
+ private void readyToCommitNavigation(NavigationImpl navigation) throws RemoteException {
+ mNavigationControllerClient.readyToCommitNavigation(navigation.getClientNavigation());
}
@CalledByNative
- private void navigationCommitted(NavigationImpl navigation) {
- try {
- mNavigationControllerClient.navigationCommitted(navigation.getClientNavigation());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call navigationCommitted.", e);
- throw new AndroidRuntimeException(e);
- }
+ private void navigationCompleted(NavigationImpl navigation) throws RemoteException {
+ mNavigationControllerClient.navigationCompleted(navigation.getClientNavigation());
}
@CalledByNative
- private void navigationCompleted(NavigationImpl navigation) {
- try {
- mNavigationControllerClient.navigationCompleted(navigation.getClientNavigation());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call navigationCompleted.", e);
- throw new AndroidRuntimeException(e);
- }
+ private void navigationFailed(NavigationImpl navigation) throws RemoteException {
+ mNavigationControllerClient.navigationFailed(navigation.getClientNavigation());
}
@CalledByNative
- private void navigationFailed(NavigationImpl navigation) {
- try {
- mNavigationControllerClient.navigationFailed(navigation.getClientNavigation());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call navigationFailed.", e);
- throw new AndroidRuntimeException(e);
- }
- }
-
- private native void nativeSetNavigationControllerImpl(long nativeNavigationControllerImpl);
-
- private static native long nativeGetNavigationController(long browserController);
- private native void nativeNavigate(long nativeNavigationControllerImpl, String uri);
- private native void nativeGoBack(long nativeNavigationControllerImpl);
- private native void nativeGoForward(long nativeNavigationControllerImpl);
- private native void nativeReload(long nativeNavigationControllerImpl);
- private native void nativeStop(long nativeNavigationControllerImpl);
- private native int nativeGetNavigationListSize(long nativeNavigationControllerImpl);
- private native int nativeGetNavigationListCurrentIndex(long nativeNavigationControllerImpl);
- private native String nativeGetNavigationEntryDisplayUri(
- long nativeNavigationControllerImpl, int index);
+ private void loadStateChanged(boolean isLoading, boolean toDifferentDocument)
+ throws RemoteException {
+ mNavigationControllerClient.loadStateChanged(isLoading, toDifferentDocument);
+ }
+
+ @CalledByNative
+ private void loadProgressChanged(double progress) throws RemoteException {
+ mNavigationControllerClient.loadProgressChanged(progress);
+ }
+
+ @CalledByNative
+ private void onFirstContentfulPaint() throws RemoteException {
+ mNavigationControllerClient.onFirstContentfulPaint();
+ }
+
+ @NativeMethods
+ interface Natives {
+ void setNavigationControllerImpl(
+ long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ long getNavigationController(long tab);
+ void navigate(
+ long nativeNavigationControllerImpl, NavigationControllerImpl caller, String uri);
+ void goBack(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ void goForward(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ boolean canGoBack(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ boolean canGoForward(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ void reload(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ void stop(long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ int getNavigationListSize(
+ long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ int getNavigationListCurrentIndex(
+ long nativeNavigationControllerImpl, NavigationControllerImpl caller);
+ String getNavigationEntryDisplayUri(
+ long nativeNavigationControllerImpl, NavigationControllerImpl caller, int index);
+ }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
index 99d6d170240..dca8e88bf87 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java
@@ -5,20 +5,25 @@
package org.chromium.weblayer_private;
import android.os.RemoteException;
-import android.util.AndroidRuntimeException;
-import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
-import org.chromium.weblayer_private.aidl.IClientNavigation;
-import org.chromium.weblayer_private.aidl.INavigation;
-import org.chromium.weblayer_private.aidl.INavigationControllerClient;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.IClientNavigation;
+import org.chromium.weblayer_private.interfaces.INavigation;
+import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
+import org.chromium.weblayer_private.interfaces.LoadError;
+import org.chromium.weblayer_private.interfaces.NavigationState;
+import java.util.Arrays;
import java.util.List;
+/**
+ * Implementation of INavigation.
+ */
@JNINamespace("weblayer")
public final class NavigationImpl extends INavigation.Stub {
- private static final String TAG = "WebLayer";
private final IClientNavigation mClientNavigation;
// WARNING: NavigationImpl may outlive the native side, in which case this member is set to 0.
private long mNativeNavigationImpl;
@@ -28,32 +33,76 @@ public final class NavigationImpl extends INavigation.Stub {
try {
mClientNavigation = client.createClientNavigation(this);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to call createClientNavigation.", e);
- throw new AndroidRuntimeException(e);
+ throw new APICallException(e);
}
- nativeSetJavaNavigation(mNativeNavigationImpl);
+ NavigationImplJni.get().setJavaNavigation(mNativeNavigationImpl, NavigationImpl.this);
}
public IClientNavigation getClientNavigation() {
return mClientNavigation;
}
+ @NavigationState
+ private static int implTypeToJavaType(@ImplNavigationState int type) {
+ switch (type) {
+ case ImplNavigationState.WAITING_RESPONSE:
+ return NavigationState.WAITING_RESPONSE;
+ case ImplNavigationState.RECEIVING_BYTES:
+ return NavigationState.RECEIVING_BYTES;
+ case ImplNavigationState.COMPLETE:
+ return NavigationState.COMPLETE;
+ case ImplNavigationState.FAILED:
+ return NavigationState.FAILED;
+ }
+ assert false;
+ return NavigationState.FAILED;
+ }
+
@Override
+ @NavigationState
public int getState() {
throwIfNativeDestroyed();
- return nativeGetState(mNativeNavigationImpl);
+ return implTypeToJavaType(
+ NavigationImplJni.get().getState(mNativeNavigationImpl, NavigationImpl.this));
}
@Override
public String getUri() {
throwIfNativeDestroyed();
- return nativeGetUri(mNativeNavigationImpl);
+ return NavigationImplJni.get().getUri(mNativeNavigationImpl, NavigationImpl.this);
}
@Override
public List<String> getRedirectChain() {
throwIfNativeDestroyed();
- return nativeGetRedirectChain(mNativeNavigationImpl);
+ return Arrays.asList(NavigationImplJni.get().getRedirectChain(
+ mNativeNavigationImpl, NavigationImpl.this));
+ }
+
+ @Override
+ public int getHttpStatusCode() {
+ throwIfNativeDestroyed();
+ return NavigationImplJni.get().getHttpStatusCode(
+ mNativeNavigationImpl, NavigationImpl.this);
+ }
+
+ @Override
+ public boolean isSameDocument() {
+ throwIfNativeDestroyed();
+ return NavigationImplJni.get().isSameDocument(mNativeNavigationImpl, NavigationImpl.this);
+ }
+
+ @Override
+ public boolean isErrorPage() {
+ throwIfNativeDestroyed();
+ return NavigationImplJni.get().isErrorPage(mNativeNavigationImpl, NavigationImpl.this);
+ }
+
+ @Override
+ public int getLoadError() {
+ throwIfNativeDestroyed();
+ return implLoadErrorToLoadError(
+ NavigationImplJni.get().getLoadError(mNativeNavigationImpl, NavigationImpl.this));
}
private void throwIfNativeDestroyed() {
@@ -62,14 +111,41 @@ public final class NavigationImpl extends INavigation.Stub {
}
}
+ @LoadError
+ private static int implLoadErrorToLoadError(@ImplLoadError int loadError) {
+ switch (loadError) {
+ case ImplLoadError.NO_ERROR:
+ return LoadError.NO_ERROR;
+ case ImplLoadError.HTTP_CLIENT_ERROR:
+ return LoadError.HTTP_CLIENT_ERROR;
+ case ImplLoadError.HTTP_SERVER_ERROR:
+ return LoadError.HTTP_SERVER_ERROR;
+ case ImplLoadError.SSL_ERROR:
+ return LoadError.SSL_ERROR;
+ case ImplLoadError.CONNECTIVITY_ERROR:
+ return LoadError.CONNECTIVITY_ERROR;
+ case ImplLoadError.OTHER_ERROR:
+ return LoadError.OTHER_ERROR;
+ default:
+ throw new IllegalArgumentException("Unexpected load error " + loadError);
+ }
+ }
+
@CalledByNative
private void onNativeDestroyed() {
mNativeNavigationImpl = 0;
// TODO: this should likely notify delegate in some way.
}
- private native void nativeSetJavaNavigation(long nativeNavigationImpl);
- private native int nativeGetState(long nativeNavigationImpl);
- private native String nativeGetUri(long nativeNavigationImpl);
- private native List<String> nativeGetRedirectChain(long nativeNavigationImpl);
+ @NativeMethods
+ interface Natives {
+ void setJavaNavigation(long nativeNavigationImpl, NavigationImpl caller);
+ int getState(long nativeNavigationImpl, NavigationImpl caller);
+ String getUri(long nativeNavigationImpl, NavigationImpl caller);
+ String[] getRedirectChain(long nativeNavigationImpl, NavigationImpl caller);
+ int getHttpStatusCode(long nativeNavigationImpl, NavigationImpl caller);
+ boolean isSameDocument(long nativeNavigationImpl, NavigationImpl caller);
+ boolean isErrorPage(long nativeNavigationImpl, NavigationImpl caller);
+ int getLoadError(long nativeNavigationImpl, NavigationImpl caller);
+ }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
new file mode 100644
index 00000000000..c5d665b7c08
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.NewTabType;
+
+/**
+ * Owns the c++ NewTabCallback class, which is responsible for forwarding all
+ * NewTabCallback calls to this class, which in turn forwards to ITabClient.
+ */
+@JNINamespace("weblayer")
+public final class NewTabCallbackProxy {
+ private long mNativeNewTabCallbackProxy;
+ private final TabImpl mTab;
+
+ public NewTabCallbackProxy(TabImpl tab) {
+ mTab = tab;
+ mNativeNewTabCallbackProxy =
+ NewTabCallbackProxyJni.get().createNewTabCallbackProxy(this, tab.getNativeTab());
+ }
+
+ public void destroy() {
+ NewTabCallbackProxyJni.get().deleteNewTabCallbackProxy(mNativeNewTabCallbackProxy);
+ mNativeNewTabCallbackProxy = 0;
+ }
+
+ @NewTabType
+ private static int implTypeToJavaType(@ImplNewTabType int type) {
+ switch (type) {
+ case ImplNewTabType.FOREGROUND:
+ return NewTabType.FOREGROUND_TAB;
+ case ImplNewTabType.BACKGROUND:
+ return NewTabType.BACKGROUND_TAB;
+ case ImplNewTabType.NEW_POPUP:
+ return NewTabType.NEW_POPUP;
+ case ImplNewTabType.NEW_WINDOW:
+ return NewTabType.NEW_WINDOW;
+ }
+ assert false;
+ return NewTabType.FOREGROUND_TAB;
+ }
+
+ @CalledByNative
+ public void onNewTab(long nativeTab, @ImplNewTabType int mode) throws RemoteException {
+ // This class should only be created while the tab is attached to a fragment.
+ assert mTab.getBrowser() != null;
+ TabImpl tab =
+ new TabImpl(mTab.getProfile(), mTab.getBrowser().getWindowAndroid(), nativeTab);
+ mTab.getBrowser().addTab(tab);
+ mTab.getClient().onNewTab(tab.getId(), mode);
+ }
+
+ @CalledByNative
+ private void onCloseTab() throws RemoteException {
+ mTab.getClient().onCloseTab();
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createNewTabCallbackProxy(NewTabCallbackProxy proxy, long tab);
+ void deleteNewTabCallbackProxy(long proxy);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/OWNERS b/chromium/weblayer/browser/java/org/chromium/weblayer_private/OWNERS
new file mode 100644
index 00000000000..49cabda246c
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/OWNERS
@@ -0,0 +1,2 @@
+# For safebrowsing related files
+per-file *GmsBridge*=timvolodine@chromium.org
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index 857777080f0..d734d7bb0c3 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -4,45 +4,87 @@
package org.chromium.weblayer_private;
-import android.content.Context;
+import androidx.annotation.NonNull;
+import org.chromium.base.CollectionUtil;
import org.chromium.base.annotations.JNINamespace;
-import org.chromium.weblayer_private.aidl.IBrowserController;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IProfile;
-import org.chromium.weblayer_private.aidl.ObjectWrapper;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.BrowsingDataType;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IProfile;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of IProfile.
+ */
@JNINamespace("weblayer")
public final class ProfileImpl extends IProfile.Stub {
+ private final String mName;
private long mNativeProfile;
+ private Runnable mOnDestroyCallback;
- public ProfileImpl(String path) {
- mNativeProfile = nativeCreateProfile(path);
+ ProfileImpl(String name, Runnable onDestroyCallback) {
+ if (!name.matches("^\\w*$")) {
+ throw new IllegalArgumentException("Name can only contain words: " + name);
+ }
+ mName = name;
+ mNativeProfile = ProfileImplJni.get().createProfile(name);
+ mOnDestroyCallback = onDestroyCallback;
}
@Override
public void destroy() {
- nativeDeleteProfile(mNativeProfile);
+ ProfileImplJni.get().deleteProfile(mNativeProfile);
mNativeProfile = 0;
+ mOnDestroyCallback.run();
+ mOnDestroyCallback = null;
}
@Override
- public void clearBrowsingData() {
- nativeClearBrowsingData(mNativeProfile);
+ public String getName() {
+ return mName;
}
@Override
- public IBrowserController createBrowserController(IObjectWrapper context) {
- return new BrowserControllerImpl(ObjectWrapper.unwrap(context, Context.class), this);
+ public void clearBrowsingData(@NonNull @BrowsingDataType int[] dataTypes, long fromMillis,
+ long toMillis, @NonNull IObjectWrapper completionCallback) {
+ Runnable callback = ObjectWrapper.unwrap(completionCallback, Runnable.class);
+ ProfileImplJni.get().clearBrowsingData(
+ mNativeProfile, mapBrowsingDataTypes(dataTypes), fromMillis, toMillis, callback);
+ }
+
+ private static @ImplBrowsingDataType int[] mapBrowsingDataTypes(
+ @NonNull @BrowsingDataType int[] dataTypes) {
+ // Convert data types coming from aidl to the ones accepted by C++ (ImplBrowsingDataType is
+ // generated from a C++ enum).
+ List<Integer> convertedTypes = new ArrayList<>();
+ for (int aidlType : dataTypes) {
+ switch (aidlType) {
+ case BrowsingDataType.COOKIES_AND_SITE_DATA:
+ convertedTypes.add(ImplBrowsingDataType.COOKIES_AND_SITE_DATA);
+ break;
+ case BrowsingDataType.CACHE:
+ convertedTypes.add(ImplBrowsingDataType.CACHE);
+ break;
+ default:
+ break; // Skip unrecognized values for forward compatibility.
+ }
+ }
+ return CollectionUtil.integerListToIntArray(convertedTypes);
}
long getNativeProfile() {
return mNativeProfile;
}
- private static native long nativeCreateProfile(String path);
-
- private static native void nativeDeleteProfile(long profile);
-
- private static native void nativeClearBrowsingData(long nativeProfileImpl);
+ @NativeMethods
+ interface Natives {
+ long createProfile(String name);
+ void deleteProfile(long profile);
+ void clearBrowsingData(long nativeProfileImpl, @ImplBrowsingDataType int[] dataTypes,
+ long fromMillis, long toMillis, Runnable callback);
+ }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java
new file mode 100644
index 00000000000..45903d5c53c
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creates and maintains the active Profiles.
+ */
+public class ProfileManager {
+ private final Map<String, ProfileImpl> mProfiles = new HashMap<>();
+
+ /** Returns existing or new Profile associated with the given name. */
+ public ProfileImpl getProfile(String name) {
+ if (name == null) throw new IllegalArgumentException("Name shouldn't be null");
+ ProfileImpl existingProfile = mProfiles.get(name);
+ if (existingProfile != null) {
+ return existingProfile;
+ }
+
+ ProfileImpl profile = new ProfileImpl(name, () -> mProfiles.remove(name));
+ mProfiles.put(name, profile);
+ return profile;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/RemoteFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/RemoteFragmentImpl.java
new file mode 100644
index 00000000000..1b4a6b9c85d
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/RemoteFragmentImpl.java
@@ -0,0 +1,247 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.view.View;
+
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IRemoteFragment;
+import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+/**
+ * Base for the classes controlling a Fragment that exists in another ClassLoader. Extending this
+ * class is similar to extending Fragment: e.g. one can override lifecycle methods, not forgetting
+ * to call super, etc.
+ */
+public abstract class RemoteFragmentImpl extends IRemoteFragment.Stub {
+ private final IRemoteFragmentClient mClient;
+
+ protected RemoteFragmentImpl(IRemoteFragmentClient client) {
+ mClient = client;
+ }
+
+ public View onCreateView() {
+ return null;
+ }
+
+ public final Activity getActivity() {
+ try {
+ return ObjectWrapper.unwrap(mClient.getActivity(), Activity.class);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ // TODO(pshmakov): add dependency to androidx.annotation and put @CallSuper here.
+ public void onCreate(Bundle savedInstanceState) {
+ try {
+ mClient.superOnCreate(ObjectWrapper.wrap(savedInstanceState));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onAttach(Context context) {
+ try {
+ mClient.superOnAttach(ObjectWrapper.wrap(context));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onActivityCreated(Bundle savedInstanceState) {
+ try {
+ mClient.superOnActivityCreated(ObjectWrapper.wrap(savedInstanceState));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {}
+
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {}
+
+ public void onStart() {
+ try {
+ mClient.superOnStart();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onDestroy() {
+ try {
+ mClient.superOnDestroy();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onDetach() {
+ try {
+ mClient.superOnDetach();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onResume() {
+ try {
+ mClient.superOnResume();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onDestroyView() {
+ try {
+ mClient.superOnDestroyView();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onStop() {
+ try {
+ mClient.superOnStop();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onPause() {
+ try {
+ mClient.superOnPause();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void onSaveInstaceState(Bundle outState) {
+ try {
+ mClient.superOnSaveInstanceState(ObjectWrapper.wrap(outState));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public boolean startActivityForResult(Intent intent, int requestCode, Bundle options) {
+ try {
+ return mClient.startActivityForResult(
+ ObjectWrapper.wrap(intent), requestCode, ObjectWrapper.wrap(options));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public boolean startIntentSenderForResult(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
+ try {
+ return mClient.startIntentSenderForResult(ObjectWrapper.wrap(intent), requestCode,
+ ObjectWrapper.wrap(fillInIntent), flagsMask, flagsValues, extraFlags,
+ ObjectWrapper.wrap(options));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public boolean shouldShowRequestPermissionRationale(String permission) {
+ try {
+ return mClient.shouldShowRequestPermissionRationale(permission);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public void requestPermissions(String[] permissions, int requestCode) {
+ try {
+ mClient.requestPermissions(permissions, requestCode);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ // IRemoteFragment implementation below.
+
+ @Override
+ public final IObjectWrapper handleOnCreateView() {
+ return ObjectWrapper.wrap(onCreateView());
+ }
+
+ @Override
+ public final void handleOnStart() {
+ onStart();
+ }
+
+ @Override
+ public final void handleOnCreate(IObjectWrapper savedInstanceState) {
+ onCreate(ObjectWrapper.unwrap(savedInstanceState, Bundle.class));
+ }
+
+ @Override
+ public final void handleOnAttach(IObjectWrapper context) {
+ onAttach(ObjectWrapper.unwrap(context, Context.class));
+ }
+
+ @Override
+ public final void handleOnActivityCreated(IObjectWrapper savedInstanceState) {
+ onActivityCreated(ObjectWrapper.unwrap(savedInstanceState, Bundle.class));
+ }
+
+ @Override
+ public final void handleOnResume() {
+ onResume();
+ }
+
+ @Override
+ public final void handleOnPause() {
+ onPause();
+ }
+
+ @Override
+ public final void handleOnStop() {
+ onStop();
+ }
+
+ @Override
+ public final void handleOnDestroyView() {
+ onDestroyView();
+ }
+
+ @Override
+ public final void handleOnDetach() {
+ onDetach();
+ }
+
+ @Override
+ public final void handleOnDestroy() {
+ onDestroy();
+ }
+
+ @Override
+ public final void handleOnSaveInstanceState(IObjectWrapper outState) {
+ onSaveInstaceState(ObjectWrapper.unwrap(outState, Bundle.class));
+ }
+
+ @Override
+ public final void handleOnActivityResult(int requestCode, int resultCode, IObjectWrapper data) {
+ onActivityResult(requestCode, resultCode, ObjectWrapper.unwrap(data, Intent.class));
+ }
+
+ @Override
+ public final void handleOnRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java
new file mode 100644
index 00000000000..4e63f53e96a
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabCallbackProxy.java
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.RemoteException;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.ITabClient;
+
+/**
+ * Owns the C++ TabCallbackProxy class, which is responsible for forwarding all
+ * BrowserObserver calls to this class, which in turn forwards to the TabClient.
+ * To avoid unnecessary IPC only one TabCallbackProxy is created per Tab.
+ */
+@JNINamespace("weblayer")
+public final class TabCallbackProxy {
+ private long mNativeTabCallbackProxy;
+ private ITabClient mClient;
+
+ TabCallbackProxy(long tab, ITabClient client) {
+ mClient = client;
+ mNativeTabCallbackProxy = TabCallbackProxyJni.get().createTabCallbackProxy(this, tab);
+ }
+
+ public void destroy() {
+ TabCallbackProxyJni.get().deleteTabCallbackProxy(mNativeTabCallbackProxy);
+ mNativeTabCallbackProxy = 0;
+ }
+
+ @CalledByNative
+ private void visibleUriChanged(String string) throws RemoteException {
+ mClient.visibleUriChanged(string);
+ }
+
+ @CalledByNative
+ private void onRenderProcessGone() throws RemoteException {
+ mClient.onRenderProcessGone();
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createTabCallbackProxy(TabCallbackProxy proxy, long tab);
+ void deleteTabCallbackProxy(long proxy);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
new file mode 100644
index 00000000000..ae439d4b688
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -0,0 +1,290 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.ValueCallback;
+
+import org.chromium.base.Callback;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.content_public.browser.SelectionPopupController;
+import org.chromium.content_public.browser.ViewEventSink;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.ViewAndroidDelegate;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
+import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
+import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
+import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ITab;
+import org.chromium.weblayer_private.interfaces.ITabClient;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+/**
+ * Implementation of ITab.
+ */
+@JNINamespace("weblayer")
+public final class TabImpl extends ITab.Stub {
+ private static int sNextId = 1;
+ private long mNativeTab;
+
+ private ProfileImpl mProfile;
+ private WebContents mWebContents;
+ private TabCallbackProxy mTabCallbackProxy;
+ private NavigationControllerImpl mNavigationController;
+ private DownloadCallbackProxy mDownloadCallbackProxy;
+ private ErrorPageCallbackProxy mErrorPageCallbackProxy;
+ private FullscreenCallbackProxy mFullscreenCallbackProxy;
+ private ViewAndroidDelegate mViewAndroidDelegate;
+ // BrowserImpl this TabImpl is in. This is only null during creation.
+ private BrowserImpl mBrowser;
+ private NewTabCallbackProxy mNewTabCallbackProxy;
+ private ITabClient mClient;
+ private final int mId;
+
+ private static class InternalAccessDelegateImpl
+ implements ViewEventSink.InternalAccessDelegate {
+ @Override
+ public boolean super_onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean super_dispatchKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean super_onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix) {}
+ }
+
+ public TabImpl(ProfileImpl profile, WindowAndroid windowAndroid) {
+ mId = ++sNextId;
+ init(profile, windowAndroid, TabImplJni.get().createTab(profile.getNativeProfile(), this));
+ }
+
+ /**
+ * This constructor is called when the native side triggers creation of a TabImpl
+ * (as happens with popups).
+ */
+ public TabImpl(ProfileImpl profile, WindowAndroid windowAndroid, long nativeTab) {
+ mId = ++sNextId;
+ TabImplJni.get().setJavaImpl(nativeTab, TabImpl.this);
+ init(profile, windowAndroid, nativeTab);
+ }
+
+ private void init(ProfileImpl profile, WindowAndroid windowAndroid, long nativeTab) {
+ mProfile = profile;
+ mNativeTab = nativeTab;
+ mWebContents = TabImplJni.get().getWebContents(mNativeTab, TabImpl.this);
+ mViewAndroidDelegate = new ViewAndroidDelegate(null) {
+ @Override
+ public void onTopControlsChanged(int topControlsOffsetY, int topContentOffsetY) {
+ BrowserViewController viewController = getViewController();
+ if (viewController != null) {
+ viewController.onTopControlsChanged(topControlsOffsetY, topContentOffsetY);
+ }
+ }
+ };
+ mWebContents.initialize("", mViewAndroidDelegate, new InternalAccessDelegateImpl(),
+ windowAndroid, WebContents.createDefaultInternalsHolder());
+ }
+
+ public ProfileImpl getProfile() {
+ return mProfile;
+ }
+
+ public ITabClient getClient() {
+ return mClient;
+ }
+
+ /**
+ * Sets the BrowserImpl this TabImpl is contained in.
+ */
+ public void attachToBrowser(BrowserImpl browser) {
+ mBrowser = browser;
+ mWebContents.setTopLevelNativeWindow(browser.getWindowAndroid());
+ mViewAndroidDelegate.setContainerView(browser.getViewAndroidDelegateContainerView());
+ SelectionPopupController.fromWebContents(mWebContents)
+ .setActionModeCallback(new ActionModeCallback(mWebContents));
+ }
+
+ public BrowserImpl getBrowser() {
+ return mBrowser;
+ }
+
+ @Override
+ public void setNewTabsEnabled(boolean enable) {
+ if (enable && mNewTabCallbackProxy == null) {
+ mNewTabCallbackProxy = new NewTabCallbackProxy(this);
+ } else if (!enable && mNewTabCallbackProxy != null) {
+ mNewTabCallbackProxy.destroy();
+ mNewTabCallbackProxy = null;
+ }
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Called when this TabImpl becomes the active TabImpl.
+ */
+ public void onDidGainActive(long topControlsContainerViewHandle) {
+ // attachToFragment() must be called before activate().
+ assert mBrowser != null;
+ TabImplJni.get().setTopControlsContainerView(
+ mNativeTab, TabImpl.this, topControlsContainerViewHandle);
+ mWebContents.onShow();
+ }
+ /**
+ * Called when this TabImpl is no longer the active TabImpl.
+ */
+ public void onDidLoseActive() {
+ mWebContents.onHide();
+ TabImplJni.get().setTopControlsContainerView(mNativeTab, TabImpl.this, 0);
+ }
+
+ public WebContents getWebContents() {
+ return mWebContents;
+ }
+
+ long getNativeTab() {
+ return mNativeTab;
+ }
+
+ @Override
+ public NavigationControllerImpl createNavigationController(INavigationControllerClient client) {
+ // This should only be called once.
+ assert mNavigationController == null;
+ mNavigationController = new NavigationControllerImpl(this, client);
+ return mNavigationController;
+ }
+
+ @Override
+ public void setClient(ITabClient client) {
+ mClient = client;
+ mTabCallbackProxy = new TabCallbackProxy(mNativeTab, client);
+ }
+
+ @Override
+ public void setDownloadCallbackClient(IDownloadCallbackClient client) {
+ if (client != null) {
+ if (mDownloadCallbackProxy == null) {
+ mDownloadCallbackProxy = new DownloadCallbackProxy(mNativeTab, client);
+ } else {
+ mDownloadCallbackProxy.setClient(client);
+ }
+ } else if (mDownloadCallbackProxy != null) {
+ mDownloadCallbackProxy.destroy();
+ mDownloadCallbackProxy = null;
+ }
+ }
+
+ @Override
+ public void setErrorPageCallbackClient(IErrorPageCallbackClient client) {
+ if (client != null) {
+ if (mErrorPageCallbackProxy == null) {
+ mErrorPageCallbackProxy = new ErrorPageCallbackProxy(mNativeTab, client);
+ } else {
+ mErrorPageCallbackProxy.setClient(client);
+ }
+ } else if (mErrorPageCallbackProxy != null) {
+ mErrorPageCallbackProxy.destroy();
+ mErrorPageCallbackProxy = null;
+ }
+ }
+
+ @Override
+ public void setFullscreenCallbackClient(IFullscreenCallbackClient client) {
+ if (client != null) {
+ if (mFullscreenCallbackProxy == null) {
+ mFullscreenCallbackProxy = new FullscreenCallbackProxy(mNativeTab, client);
+ } else {
+ mFullscreenCallbackProxy.setClient(client);
+ }
+ } else if (mFullscreenCallbackProxy != null) {
+ mFullscreenCallbackProxy.destroy();
+ mFullscreenCallbackProxy = null;
+ }
+ }
+
+ @Override
+ public void executeScript(String script, boolean useSeparateIsolate, IObjectWrapper callback) {
+ Callback<String> nativeCallback = new Callback<String>() {
+ @Override
+ public void onResult(String result) {
+ ValueCallback<String> unwrappedCallback =
+ (ValueCallback<String>) ObjectWrapper.unwrap(callback, ValueCallback.class);
+ if (unwrappedCallback != null) {
+ unwrappedCallback.onReceiveValue(result);
+ }
+ }
+ };
+ TabImplJni.get().executeScript(mNativeTab, script, useSeparateIsolate, nativeCallback);
+ }
+
+ public void destroy() {
+ if (mTabCallbackProxy != null) {
+ mTabCallbackProxy.destroy();
+ mTabCallbackProxy = null;
+ }
+ if (mDownloadCallbackProxy != null) {
+ mDownloadCallbackProxy.destroy();
+ mDownloadCallbackProxy = null;
+ }
+ if (mErrorPageCallbackProxy != null) {
+ mErrorPageCallbackProxy.destroy();
+ mErrorPageCallbackProxy = null;
+ }
+ if (mFullscreenCallbackProxy != null) {
+ mFullscreenCallbackProxy.destroy();
+ mFullscreenCallbackProxy = null;
+ }
+ if (mNewTabCallbackProxy != null) {
+ mNewTabCallbackProxy.destroy();
+ mNewTabCallbackProxy = null;
+ }
+ mNavigationController = null;
+ TabImplJni.get().deleteTab(mNativeTab);
+ mNativeTab = 0;
+ }
+
+ @CalledByNative
+ private boolean doBrowserControlsShrinkRendererSize() {
+ BrowserViewController viewController = getViewController();
+ return viewController != null && viewController.doBrowserControlsShrinkRendererSize();
+ }
+
+ /**
+ * Returns the BrowserViewController for this TabImpl, but only if this
+ * is the active TabImpl.
+ */
+ private BrowserViewController getViewController() {
+ return (mBrowser.getActiveTab() == this) ? mBrowser.getViewController() : null;
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createTab(long profile, TabImpl caller);
+ void setJavaImpl(long nativeTabImpl, TabImpl impl);
+ void setTopControlsContainerView(
+ long nativeTabImpl, TabImpl caller, long nativeTopControlsContainerView);
+ void deleteTab(long tab);
+ WebContents getWebContents(long nativeTabImpl, TabImpl caller);
+ void executeScript(long nativeTabImpl, String script, boolean useSeparateIsolate,
+ Callback<String> callback);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
new file mode 100644
index 00000000000..c2c1f9d5d25
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
@@ -0,0 +1,351 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.EventOffsetHandler;
+import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
+
+/**
+ * TopControlsContainerView is responsible for holding the top-view from the client. Further, it
+ * has a ViewResourceAdapter that is kept in sync with the contents of the top-view.
+ * ViewResourceAdapter is used to keep a bitmap in sync with the contents of the top-view. The
+ * bitmap is placed in a cc::Layer and the layer is shown while scrolling the top-view.
+ * ViewResourceAdapter is always kept in sync, as to do otherwise results in a noticeable delay
+ * between when the scroll starts the content is available.
+ *
+ * There are many parts involved in orchestrating top-controls scrolling. The key things to know
+ * are:
+ * . TopControlsContainerView (in native code) keeps a cc::Layer that shows a bitmap rendered by
+ * the top-view. The bitmap is updated anytime the top-view changes. This is done as otherwise
+ * there is a noticable delay between when the scroll starts and the bitmap is available.
+ * . When scrolling, the cc::Layer for the WebContents and TopControlsContainerView is moved.
+ * . The size of the WebContents is only changed after the user releases a touch point. Otherwise
+ * the scrollbar bounces around.
+ * . WebContentsDelegate::DoBrowserControlsShrinkRendererSize() only changes when the WebContents
+ * size change.
+ * . WebContentsGestureStateTracker is responsible for determining when a scroll/touch is underway.
+ * . ContentViewRenderView.Delegate is used to adjust the size of the webcontents when the
+ * top-controls are fully visible (and a scroll is not underway).
+ *
+ * The flow of this code is roughly:
+ * . WebContentsGestureStateTracker generally detects a touch first
+ * . TabImpl is notified and caches state.
+ * . onTopControlsChanged() is called. This triggers hiding the real view and calling to native code
+ * to move the cc::Layers.
+ * . the move continues.
+ * . when the move completes and both WebContentsGestureStateTracker and TopControlsContainerView
+ * no longer believe a move/gesture/scroll is underway the size of the WebContents is adjusted
+ * (if necessary).
+ */
+@JNINamespace("weblayer")
+class TopControlsContainerView extends FrameLayout {
+ // ID used with ViewResourceAdapter.
+ private static final int TOP_CONTROLS_ID = 1001;
+
+ private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500;
+
+ private long mNativeTopControlsContainerView;
+
+ private ViewResourceAdapter mViewResourceAdapter;
+
+ // Last width/height of mView as sent to the native side.
+ private int mLastWidth;
+ private int mLastHeight;
+
+ // view from the client.
+ private View mView;
+
+ private ContentViewRenderView mContentViewRenderView;
+ private WebContents mWebContents;
+ private EventOffsetHandler mEventOffsetHandler;
+ private int mTopContentOffset;
+
+ // Set to true if |mView| is hidden because the user has scrolled or triggered some action such
+ // that mView is not visible. While |mView| is not visible if this is true, the bitmap from
+ // |mView| may be partially visible.
+ private boolean mInTopControlsScroll;
+
+ private boolean mIsFullscreen;
+
+ // Used to delay processing fullscreen requests.
+ private Runnable mSystemUiFullscreenResizeRunnable;
+
+ private final Listener mListener;
+
+ public interface Listener {
+ /**
+ * Called when the top-controls are either completely showing, or completely hiding.
+ */
+ public void onTopControlsCompletelyShownOrHidden();
+ }
+
+ // Used to delay updating the image for the layer.
+ private final Runnable mRefreshResourceIdRunnable = () -> {
+ if (mView == null) return;
+ TopControlsContainerViewJni.get().updateTopControlsResource(
+ mNativeTopControlsContainerView, TopControlsContainerView.this);
+ };
+
+ TopControlsContainerView(
+ Context context, ContentViewRenderView contentViewRenderView, Listener listener) {
+ super(context);
+ mContentViewRenderView = contentViewRenderView;
+ mEventOffsetHandler =
+ new EventOffsetHandler(new EventOffsetHandler.EventOffsetHandlerDelegate() {
+ @Override
+ public float getTop() {
+ return mTopContentOffset;
+ }
+
+ @Override
+ public void setCurrentTouchEventOffsets(float top) {
+ if (mWebContents != null) {
+ mWebContents.getEventForwarder().setCurrentTouchEventOffsets(0, top);
+ }
+ }
+ });
+ mNativeTopControlsContainerView =
+ TopControlsContainerViewJni.get().createTopControlsContainerView(
+ this, contentViewRenderView.getNativeHandle());
+ mListener = listener;
+ }
+
+ public void setWebContents(WebContents webContents) {
+ mWebContents = webContents;
+ TopControlsContainerViewJni.get().setWebContents(
+ mNativeTopControlsContainerView, TopControlsContainerView.this, webContents);
+ }
+
+ public void destroy() {
+ setView(null);
+ TopControlsContainerViewJni.get().deleteTopControlsContainerView(
+ mNativeTopControlsContainerView, TopControlsContainerView.this);
+ }
+
+ public long getNativeHandle() {
+ return mNativeTopControlsContainerView;
+ }
+
+ public EventOffsetHandler getEventOffsetHandler() {
+ return mEventOffsetHandler;
+ }
+
+ /**
+ * Returns the vertical offset for the WebContents.
+ */
+ public int getTopContentOffset() {
+ return mView == null ? 0 : mTopContentOffset;
+ }
+
+ /**
+ * Returns true if the top control is visible to the user.
+ */
+ public boolean isTopControlVisible() {
+ // Don't check the visibility of the View itself as it's hidden while scrolling.
+ return mView != null && mTopContentOffset != 0;
+ }
+
+ /**
+ * Sets the view from the client.
+ */
+ public void setView(View view) {
+ if (mView == view) return;
+ if (mView != null) {
+ if (mView.getParent() == this) removeView(mView);
+ // TODO: need some sort of destroy to drop reference.
+ mViewResourceAdapter = null;
+ TopControlsContainerViewJni.get().deleteTopControlsLayer(
+ mNativeTopControlsContainerView, TopControlsContainerView.this);
+ mContentViewRenderView.getResourceManager()
+ .getDynamicResourceLoader()
+ .unregisterResource(TOP_CONTROLS_ID);
+ }
+ mView = view;
+ if (mView == null) return;
+ addView(view,
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY));
+ if (getWidth() > 0 && getHeight() > 0) {
+ view.layout(0, 0, getWidth(), getHeight());
+ createAdapterAndLayer();
+ }
+ if (mIsFullscreen) hideTopControls();
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ /**
+ * Called from ViewAndroidDelegate, see it for details.
+ */
+ public void onTopControlsChanged(int topControlsOffsetY, int topContentOffsetY) {
+ if (mView == null) return;
+ if (mIsFullscreen) return;
+ if (topContentOffsetY == getHeight()) {
+ finishTopControlsScroll(topContentOffsetY);
+ return;
+ }
+ if (!mInTopControlsScroll) prepareForTopControlsScroll();
+ setTopControlsOffset(topControlsOffsetY, topContentOffsetY);
+ }
+
+ @SuppressLint("NewApi") // Used on O+, invalidateChildInParent used for previous versions.
+ @Override
+ public void onDescendantInvalidated(View child, View target) {
+ super.onDescendantInvalidated(child, target);
+ invalidateViewResourceAdapter();
+ }
+
+ @Override
+ public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+ invalidateViewResourceAdapter();
+ return super.invalidateChildInParent(location, dirty);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mView == null) return;
+ int width = right - left;
+ int height = bottom - top;
+ if (height != mLastHeight || width != mLastWidth) {
+ mLastWidth = width;
+ mLastHeight = height;
+ if (mLastWidth > 0 && mLastHeight > 0) {
+ if (mViewResourceAdapter == null) {
+ createAdapterAndLayer();
+ } else {
+ TopControlsContainerViewJni.get().setTopControlsSize(
+ mNativeTopControlsContainerView, TopControlsContainerView.this,
+ mLastWidth, mLastHeight);
+ }
+ }
+ }
+ }
+
+ /**
+ * Triggers copying the contents of mView to the offscreen buffer.
+ */
+ private void invalidateViewResourceAdapter() {
+ if (mViewResourceAdapter == null || mView.getVisibility() != View.VISIBLE) return;
+ mViewResourceAdapter.invalidate(null);
+ removeCallbacks(mRefreshResourceIdRunnable);
+ postOnAnimation(mRefreshResourceIdRunnable);
+ }
+
+ /**
+ * Creates mViewResourceAdapter and the layer showing a copy of mView.
+ */
+ private void createAdapterAndLayer() {
+ assert mViewResourceAdapter == null;
+ assert mView != null;
+ mViewResourceAdapter = new ViewResourceAdapter(mView);
+ mContentViewRenderView.getResourceManager().getDynamicResourceLoader().registerResource(
+ TOP_CONTROLS_ID, mViewResourceAdapter);
+ // It's important that the layer is created immediately and always kept in sync with the
+ // View. Creating the layer only when needed results in a noticeable delay between when
+ // the layer is created and actually shown. Chrome for Android does the same thing.
+ TopControlsContainerViewJni.get().createTopControlsLayer(
+ mNativeTopControlsContainerView, TopControlsContainerView.this, TOP_CONTROLS_ID);
+ mLastWidth = getWidth();
+ mLastHeight = getHeight();
+ TopControlsContainerViewJni.get().setTopControlsSize(mNativeTopControlsContainerView,
+ TopControlsContainerView.this, mLastWidth, mLastHeight);
+ }
+
+ private void finishTopControlsScroll(int topContentOffsetY) {
+ mInTopControlsScroll = false;
+ setTopControlsOffset(0, topContentOffsetY);
+ mContentViewRenderView.postOnAnimation(() -> showTopControls());
+ }
+
+ /**
+ * Returns true if the top-controls are completely shown or completely hidden. A return value
+ * of false indicates the top-controls are being moved.
+ */
+ public boolean isTopControlsCompletelyShownOrHidden() {
+ return mTopContentOffset == 0 || mTopContentOffset == getHeight();
+ }
+
+ private void setTopControlsOffset(int topControlsOffsetY, int topContentOffsetY) {
+ mTopContentOffset = topContentOffsetY;
+ if (isTopControlsCompletelyShownOrHidden()) {
+ mListener.onTopControlsCompletelyShownOrHidden();
+ }
+ TopControlsContainerViewJni.get().setTopControlsOffset(mNativeTopControlsContainerView,
+ TopControlsContainerView.this, topControlsOffsetY, topContentOffsetY);
+ }
+
+ private void prepareForTopControlsScroll() {
+ mInTopControlsScroll = true;
+ mContentViewRenderView.postOnAnimation(() -> hideTopControls());
+ }
+
+ private void hideTopControls() {
+ if (mView != null) mView.setVisibility(View.INVISIBLE);
+ }
+
+ private void showTopControls() {
+ if (mView != null) mView.setVisibility(View.VISIBLE);
+ }
+
+ @CalledByNative
+ private void didToggleFullscreenModeForTab(final boolean isFullscreen) {
+ // Delay hiding until after the animation. This comes from Chrome code.
+ if (mSystemUiFullscreenResizeRunnable != null) {
+ getHandler().removeCallbacks(mSystemUiFullscreenResizeRunnable);
+ }
+ mSystemUiFullscreenResizeRunnable = () -> processFullscreenChanged(isFullscreen);
+ long delay = isFullscreen ? SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS : 0;
+ postDelayed(mSystemUiFullscreenResizeRunnable, delay);
+ }
+
+ private void processFullscreenChanged(boolean isFullscreen) {
+ mSystemUiFullscreenResizeRunnable = null;
+ if (mIsFullscreen == isFullscreen) return;
+ mIsFullscreen = isFullscreen;
+ if (mView == null) return;
+ if (mIsFullscreen) {
+ hideTopControls();
+ setTopControlsOffset(-mLastHeight, 0);
+ } else {
+ showTopControls();
+ setTopControlsOffset(0, mLastHeight);
+ }
+ }
+
+ @NativeMethods
+ interface Natives {
+ long createTopControlsContainerView(
+ TopControlsContainerView view, long nativeContentViewRenderView);
+ void deleteTopControlsContainerView(
+ long nativeTopControlsContainerView, TopControlsContainerView caller);
+ void createTopControlsLayer(
+ long nativeTopControlsContainerView, TopControlsContainerView caller, int id);
+ void deleteTopControlsLayer(
+ long nativeTopControlsContainerView, TopControlsContainerView caller);
+ void setTopControlsOffset(long nativeTopControlsContainerView,
+ TopControlsContainerView caller, int topControlsOffsetY, int topContentOffsetY);
+ void setTopControlsSize(long nativeTopControlsContainerView,
+ TopControlsContainerView caller, int width, int height);
+ void updateTopControlsResource(
+ long nativeTopControlsContainerView, TopControlsContainerView caller);
+ void setWebContents(long nativeTopControlsContainerView, TopControlsContainerView caller,
+ WebContents webContents);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebContentsGestureStateTracker.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebContentsGestureStateTracker.java
new file mode 100644
index 00000000000..eb9ca9b1ddd
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebContentsGestureStateTracker.java
@@ -0,0 +1,110 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.chromium.content_public.browser.GestureListenerManager;
+import org.chromium.content_public.browser.GestureStateListener;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * WebContentsGestureStateTracker is responsible for tracking when a scroll/gesture is in progress
+ * and notifying when the state changes.
+ */
+// TODO(sky): refactor TabGestureStateListener and this to a common place.
+public final class WebContentsGestureStateTracker {
+ private GestureListenerManager mGestureListenerManager;
+ private GestureStateListener mGestureListener;
+ private final OnGestureStateChangedListener mListener;
+ private boolean mScrolling;
+ private boolean mIsInGesture;
+
+ /**
+ * The View events are tracked on.
+ */
+ private View mContentView;
+
+ /**
+ * Notified when the gesture state changes.
+ */
+ public interface OnGestureStateChangedListener {
+ /**
+ * Called when the value of isInGestureOrScroll() changes.
+ */
+ public void onGestureStateChanged();
+ }
+
+ public WebContentsGestureStateTracker(
+ View contentView, WebContents webContents, OnGestureStateChangedListener listener) {
+ mListener = listener;
+ mGestureListenerManager = GestureListenerManager.fromWebContents(webContents);
+ mContentView = contentView;
+ mContentView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ final int eventAction = event.getActionMasked();
+ final boolean oldState = isInGestureOrScroll();
+ if (eventAction == MotionEvent.ACTION_DOWN
+ || eventAction == MotionEvent.ACTION_POINTER_DOWN) {
+ mIsInGesture = true;
+ } else if (eventAction == MotionEvent.ACTION_CANCEL
+ || eventAction == MotionEvent.ACTION_UP) {
+ mIsInGesture = false;
+ }
+ if (isInGestureOrScroll() != oldState) {
+ mListener.onGestureStateChanged();
+ }
+ return false;
+ }
+ });
+
+ mGestureListener = new GestureStateListener() {
+ @Override
+ public void onFlingStartGesture(int scrollOffsetY, int scrollExtentY) {
+ onScrollingStateChanged();
+ }
+
+ @Override
+ public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
+ onScrollingStateChanged();
+ }
+
+ @Override
+ public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
+ onScrollingStateChanged();
+ }
+
+ @Override
+ public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
+ onScrollingStateChanged();
+ }
+
+ private void onScrollingStateChanged() {
+ final boolean oldState = isInGestureOrScroll();
+ mScrolling = mGestureListenerManager.isScrollInProgress();
+ if (oldState != isInGestureOrScroll()) {
+ mListener.onGestureStateChanged();
+ }
+ }
+ };
+ mGestureListenerManager.addListener(mGestureListener);
+ }
+
+ public void destroy() {
+ mGestureListenerManager.removeListener(mGestureListener);
+ mGestureListener = null;
+ mGestureListenerManager = null;
+ mContentView.setOnTouchListener(null);
+ }
+
+ /**
+ * Returns true if the user has touched the target view, or is scrolling.
+ */
+ public boolean isInGestureOrScroll() {
+ return mIsInGesture || mScrolling;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java
new file mode 100644
index 00000000000..68b11bcb5f2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.os.IBinder;
+
+import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.components.version_info.VersionConstants;
+import org.chromium.weblayer_private.interfaces.IWebLayer;
+import org.chromium.weblayer_private.interfaces.IWebLayerFactory;
+import org.chromium.weblayer_private.interfaces.WebLayerVersion;
+
+/**
+ * Factory used to create WebLayer as well as verify compatibility.
+ * This is constructed by the client library using reflection.
+ */
+@UsedByReflection("WebLayer")
+public final class WebLayerFactoryImpl extends IWebLayerFactory.Stub {
+ private final int mClientMajorVersion;
+ private final String mClientVersion;
+ private final int mClientWeblayerVersion;
+
+ /**
+ * This function is called by the client using reflection.
+ *
+ * @param clientVersion The full version string the client was compiled from.
+ * @param clientMajorVersion The major version number the client was compiled from. This is also
+ * contained in clientVersion.
+ * @param clientWebLayerVersion The version from interfaces.WebLayerVersion the client was
+ * compiled with.
+ */
+ @UsedByReflection("WebLayer")
+ public static IBinder create(
+ String clientVersion, int clientMajorVersion, int clientWebLayerVersion) {
+ return new WebLayerFactoryImpl(clientVersion, clientMajorVersion, clientWebLayerVersion);
+ }
+
+ private WebLayerFactoryImpl(
+ String clientVersion, int clientMajorVersion, int clientWeblayerVersion) {
+ mClientMajorVersion = clientMajorVersion;
+ mClientVersion = clientVersion;
+ mClientWeblayerVersion = clientWeblayerVersion;
+ }
+
+ /**
+ * Returns true if the client compiled with the specific version is compatible with this
+ * implementation. The client library calls this exactly once.
+ */
+ @Override
+ public boolean isClientSupported() {
+ return mClientWeblayerVersion == WebLayerVersion.sVersionNumber;
+ }
+
+ /**
+ * Returns the major version of the implementation.
+ */
+ @Override
+ public int getImplementationMajorVersion() {
+ return VersionConstants.PRODUCT_MAJOR_VERSION;
+ }
+
+ /**
+ * Returns the full version string of the implementation.
+ */
+ @Override
+ public String getImplementationVersion() {
+ return VersionConstants.PRODUCT_VERSION;
+ }
+
+ @Override
+ public IWebLayer createWebLayer() {
+ assert isClientSupported();
+ return new WebLayerImpl();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 7f6069e8eda..5ba843f2e25 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -4,80 +4,253 @@
package org.chromium.weblayer_private;
-import android.app.Application;
import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
-import android.os.IBinder;
-import android.util.AndroidRuntimeException;
+import android.content.pm.PackageInfo;
+import android.content.res.AssetManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.content.FileProvider;
+import android.util.SparseArray;
+import android.webkit.ValueCallback;
+import android.webkit.WebViewDelegate;
+import android.webkit.WebViewFactory;
-import org.chromium.base.ApplicationStatus;
+import org.chromium.base.BuildInfo;
import org.chromium.base.CommandLine;
+import org.chromium.base.ContentUriUtils;
import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
import org.chromium.base.PathUtils;
+import org.chromium.base.StrictModeContext;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
import org.chromium.content_public.browser.BrowserStartupController;
import org.chromium.content_public.browser.ChildProcessCreationParams;
import org.chromium.content_public.browser.DeviceUtils;
import org.chromium.ui.base.ResourceBundle;
-import org.chromium.weblayer_private.aidl.IProfile;
-import org.chromium.weblayer_private.aidl.IWebLayer;
+import org.chromium.weblayer_private.interfaces.IBrowserFragment;
+import org.chromium.weblayer_private.interfaces.ICrashReporterController;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IProfile;
+import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
+import org.chromium.weblayer_private.interfaces.IWebLayer;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Root implementation class for WebLayer.
+ */
+@JNINamespace("weblayer")
public final class WebLayerImpl extends IWebLayer.Stub {
// TODO: should there be one tag for all this code?
private static final String TAG = "WebLayer";
- private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "weblayer";
+ private static final String PRIVATE_DIRECTORY_SUFFIX = "weblayer";
// TODO: Configure this from the client.
private static final String COMMAND_LINE_FILE = "/data/local/tmp/weblayer-command-line";
- public static IBinder create(Application application, Context implContext) {
- return new WebLayerImpl(application, implContext);
+ private final ProfileManager mProfileManager = new ProfileManager();
+
+ private boolean mInited;
+
+ private static class FileProviderHelper implements ContentUriUtils.FileProviderUtil {
+ // Keep this variable in sync with the value defined in AndroidManifest.xml.
+ private static final String API_AUTHORITY_SUFFIX =
+ ".org.chromium.weblayer.client.FileProvider";
+
+ @Override
+ public Uri getContentUriFromFile(File file) {
+ Context appContext = ContextUtils.getApplicationContext();
+ return FileProvider.getUriForFile(
+ appContext, appContext.getPackageName() + API_AUTHORITY_SUFFIX, file);
+ }
+ }
+
+ WebLayerImpl() {}
+
+ /**
+ * Performs the minimal initialization needed for a context. This is used for example in
+ * CrashReporterControllerImpl, so it can be used before full WebLayer initialization.
+ */
+ public static Context minimalInitForContext(IObjectWrapper appContextWrapper) {
+ if (ContextUtils.getApplicationContext() != null) {
+ return ContextUtils.getApplicationContext();
+ }
+ // Wrap the app context so that it can be used to load WebLayer implementation classes.
+ Context appContext = ClassLoaderContextWrapperFactory.get(
+ ObjectWrapper.unwrap(appContextWrapper, Context.class));
+ ContextUtils.initApplicationContext(appContext);
+ PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DIRECTORY_SUFFIX, PRIVATE_DIRECTORY_SUFFIX);
+ return appContext;
}
@Override
- public IProfile createProfile(String path) {
- return new ProfileImpl(path);
+ public void loadAsync(
+ IObjectWrapper appContextWrapper, IObjectWrapper loadedCallbackWrapper) {
+ init(appContextWrapper);
+
+ final ValueCallback<Boolean> loadedCallback = (ValueCallback<Boolean>) ObjectWrapper.unwrap(
+ loadedCallbackWrapper, ValueCallback.class);
+ BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER)
+ .startBrowserProcessesAsync(/* startGpu */ false,
+ /* startServiceManagerOnly */ false,
+ new BrowserStartupController.StartupCallback() {
+ @Override
+ public void onSuccess() {
+ CrashReporterControllerImpl.getInstance(appContextWrapper)
+ .notifyNativeInitialized();
+ loadedCallback.onReceiveValue(true);
+ }
+ @Override
+ public void onFailure() {
+ loadedCallback.onReceiveValue(false);
+ }
+ });
}
- private WebLayerImpl(Application application, Context implContext) {
- ContextUtils.initApplicationContext(new ContextWrapper(application) {
- @Override
- public Resources getResources() {
- // Always use resources from the implementation APK.
- return implContext.getResources();
- }
- });
- ResourceBundle.setNoAvailableLocalePaks();
- PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
- ApplicationStatus.initialize(application);
+ @Override
+ public void loadSync(IObjectWrapper appContextWrapper) {
+ init(appContextWrapper);
- ChildProcessCreationParams.set(implContext.getPackageName(), true /* isExternalService */,
- LibraryProcessType.PROCESS_CHILD, true /* bindToCaller */,
- false /* ignoreVisibilityForImportance */);
+ BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER)
+ .startBrowserProcessesSync(
+ /* singleProcess*/ false);
+ CrashReporterControllerImpl.getInstance(appContextWrapper).notifyNativeInitialized();
+ }
+
+ private void init(IObjectWrapper appContextWrapper) {
+ if (mInited) {
+ return;
+ }
+ mInited = true;
+
+ Context appContext = minimalInitForContext(appContextWrapper);
+ PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo();
+
+ // TODO: This can break some functionality of apps that are doing interesting things with
+ // Contexts, ideally we would find a better way to do this.
+ addWebViewAssetPath(appContext, packageInfo);
+
+ BuildInfo.setBrowserPackageInfo(packageInfo);
+ int resourcesPackageId = getPackageId(appContext, packageInfo.packageName);
+ // TODO: The call to onResourcesLoaded() can be slow, we may need to parallelize this with
+ // other expensive startup tasks.
+ R.onResourcesLoaded(resourcesPackageId);
+
+ ResourceBundle.setAvailablePakLocales(new String[] {}, LocaleConfig.UNCOMPRESSED_LOCALES);
+
+ ChildProcessCreationParams.set(appContext.getPackageName(), false /* isExternalService */,
+ LibraryProcessType.PROCESS_WEBLAYER_CHILD, true /* bindToCaller */,
+ false /* ignoreVisibilityForImportance */,
+ "org.chromium.weblayer.ChildProcessService$Privileged",
+ "org.chromium.weblayer.ChildProcessService$Sandboxed");
if (!CommandLine.isInitialized()) {
- CommandLine.initFromFile(COMMAND_LINE_FILE);
+ // This disk read in the critical path is for development purposes only.
+ // TODO: Move it to debug-only (similar to WebView), or allow clients to configure the
+ // command line.
+ try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+ CommandLine.initFromFile(COMMAND_LINE_FILE);
+ }
}
DeviceUtils.addDeviceSpecificUserAgentSwitch();
+ ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
+ // TODO: Validate that doing this disk IO on the main thread is necessary.
+ try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+ LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_WEBLAYER);
+ }
+ GmsBridge.getInstance().setSafeBrowsingHandler();
+ }
+
+ @Override
+ public IBrowserFragment createBrowserFragmentImpl(
+ IRemoteFragmentClient fragmentClient, IObjectWrapper fragmentArgs) {
+ Bundle unwrappedArgs = ObjectWrapper.unwrap(fragmentArgs, Bundle.class);
+ BrowserFragmentImpl fragment =
+ new BrowserFragmentImpl(mProfileManager, fragmentClient, unwrappedArgs);
+ return fragment.asIBrowserFragment();
+ }
+
+ @Override
+ public IProfile getProfile(String profileName) {
+ return mProfileManager.getProfile(profileName);
+ }
+
+ @Override
+ public void setRemoteDebuggingEnabled(boolean enabled) {
+ WebLayerImplJni.get().setRemoteDebuggingEnabled(enabled);
+ }
+
+ @Override
+ public boolean isRemoteDebuggingEnabled() {
+ return WebLayerImplJni.get().isRemoteDebuggingEnabled();
+ }
+
+ @Override
+ public ICrashReporterController getCrashReporterController(IObjectWrapper appContext) {
+ return CrashReporterControllerImpl.getInstance(appContext);
+ }
+
+ private static void addWebViewAssetPath(Context appContext, PackageInfo packageInfo) {
try {
- LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
- } catch (ProcessInitException e) {
- Log.e(TAG, "ContentView initialization failed.", e);
- throw new AndroidRuntimeException(e);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ Constructor constructor = WebViewDelegate.class.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ WebViewDelegate delegate = (WebViewDelegate) constructor.newInstance();
+ delegate.addWebViewAssetPath(appContext);
+ } else {
+ // In L WebViewDelegate did not yet exist, so we have to poke AssetManager directly.
+ // Note: like the implementation in WebView's Api21CompatibilityDelegate this does
+ // not support split APKs.
+ Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
+ addAssetPath.invoke(appContext.getResources().getAssets(),
+ packageInfo.applicationInfo.sourceDir);
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
}
+ }
+ /**
+ * Returns the package ID to use when calling R.onResourcesLoaded().
+ */
+ private static int getPackageId(Context appContext, String implPackageName) {
try {
- BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
- .startBrowserProcessesSync(
- /* singleProcess*/ false);
- } catch (ProcessInitException e) {
- Log.e(TAG, "Unable to load native library.", e);
- throw new AndroidRuntimeException(e);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ Constructor constructor = WebViewDelegate.class.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ WebViewDelegate delegate = (WebViewDelegate) constructor.newInstance();
+ return delegate.getPackageId(appContext.getResources(), implPackageName);
+ } else {
+ // In L WebViewDelegate did not yet exist, so we have to look inside AssetManager.
+ Method getAssignedPackageIdentifiers =
+ AssetManager.class.getMethod("getAssignedPackageIdentifiers");
+ SparseArray packageIdentifiers = (SparseArray) getAssignedPackageIdentifiers.invoke(
+ appContext.getResources().getAssets());
+ for (int i = 0; i < packageIdentifiers.size(); i++) {
+ final String name = (String) packageIdentifiers.valueAt(i);
+
+ if (implPackageName.equals(name)) {
+ return packageIdentifiers.keyAt(i);
+ }
+ }
+ throw new RuntimeException("Package not found: " + implPackageName);
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
}
}
+
+ @NativeMethods
+ interface Natives {
+ void setRemoteDebuggingEnabled(boolean enabled);
+ boolean isRemoteDebuggingEnabled();
+ }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
deleted file mode 100644
index 778e0445d60..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserController.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private.aidl;
-
-import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
-
-interface IBrowserController {
- void setClient(in IBrowserControllerClient client) = 0;
-
- INavigationController createNavigationController(in INavigationControllerClient client) = 1;
-
- void setTopView(in IObjectWrapper view) = 2;
-
- void destroy() = 3;
-
- IObjectWrapper onCreateView() = 4;
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
deleted file mode 100644
index 11f52522106..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private.aidl;
-
-/**
- * Interface used by BrowserController to inform the client of changes. This largely duplicates the
- * BrowserObserver interface, but is a singleton to avoid unnecessary IPC.
- */
-interface IBrowserControllerClient {
- /** The Uri that should be displayed in the url-bar has updated. */
- void displayURLChanged(in String url) = 0;
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
deleted file mode 100644
index d68789343ad..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private.aidl;
-
-import org.chromium.weblayer_private.aidl.IBrowserController;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
-
-interface IProfile {
- void destroy() = 0;
-
- void clearBrowsingData() = 1;
-
- IBrowserController createBrowserController(in IObjectWrapper context) = 2;
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl
deleted file mode 100644
index 7625d7b6bf7..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.weblayer_private.aidl;
-
-import org.chromium.weblayer_private.aidl.IProfile;
-
-interface IWebLayer {
- IProfile createProfile(in String path) = 0;
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/APICallException.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/APICallException.java
new file mode 100644
index 00000000000..59366a751c7
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/APICallException.java
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import android.util.AndroidRuntimeException;
+
+/**
+ * Error thrown if there is an error communicating over the AIDL boundary.
+ */
+public class APICallException extends AndroidRuntimeException {
+ /**
+ * Constructs a new exception with the specified cause.
+ */
+ public APICallException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java
new file mode 100644
index 00000000000..cefc545ded7
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+/** Keys for the Bundle of arguments with which BrowserFragments are created. */
+public interface BrowserFragmentArgs { String PROFILE_NAME = "profile_name"; }
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java
new file mode 100644
index 00000000000..840bfae909c
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({BrowsingDataType.COOKIES_AND_SITE_DATA, BrowsingDataType.CACHE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface BrowsingDataType {
+ int COOKIES_AND_SITE_DATA = 0;
+ int CACHE = 1;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
new file mode 100644
index 00000000000..c262dee3c34
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IBrowserClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ITab;
+import org.chromium.weblayer_private.interfaces.IProfile;
+
+import java.util.List;
+
+interface IBrowser {
+ IProfile getProfile() = 0;
+ void setTopView(in IObjectWrapper view) = 1;
+
+ // |valueCallback| is a wrapped ValueCallback<Boolean> instead. The bool value in |valueCallback|
+ // indicates is whether the request was successful. Request might fail if it is subsumed by a
+ // following request, or if this object is destroyed.
+ void setSupportsEmbedding(in boolean enable, in IObjectWrapper valueCallback) = 2;
+
+ // Sets the active tab, returns false if tab is not attached to this fragment.
+ boolean setActiveTab(in ITab tab) = 3;
+
+ int getActiveTabId() = 4;
+ List getTabs() = 5;
+
+ void setClient(in IBrowserClient client) = 6;
+
+ void addTab(in ITab tab) = 7;
+ void destroyTab(in ITab tab) = 8;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
new file mode 100644
index 00000000000..c23221efd1d
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.ITab;
+
+interface IBrowserClient {
+ void onActiveTabChanged(in int activeTabId) = 0;
+ void onTabAdded(in ITab tab) = 1;
+ void onTabRemoved(in int tabId) = 2;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl
new file mode 100644
index 00000000000..7b7d3b218e8
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IBrowser;
+import org.chromium.weblayer_private.interfaces.IRemoteFragment;
+
+interface IBrowserFragment {
+ IRemoteFragment asRemoteFragment() = 0;
+ IBrowser getBrowser() = 1;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IChildProcessService.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IChildProcessService.aidl
new file mode 100644
index 00000000000..87402311731
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IChildProcessService.aidl
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+/** Interface to forward service calls to the service implementation. */
+interface IChildProcessService {
+ void onCreate() = 0;
+
+ void onDestroy() = 1;
+
+ IObjectWrapper onBind(IObjectWrapper intent) = 2;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IClientNavigation.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IClientNavigation.aidl
index 442060c0335..2adeaa34148 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IClientNavigation.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IClientNavigation.aidl
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
/**
* Represents a navigation on the *client* side.
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterController.aidl
new file mode 100644
index 00000000000..04e963521b3
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterController.aidl
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import android.os.Bundle;
+
+import org.chromium.weblayer_private.interfaces.ICrashReporterControllerClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+interface ICrashReporterController {
+ void setClient(in ICrashReporterControllerClient client) = 0;
+ void checkForPendingCrashReports() = 1;
+ Bundle getCrashKeys(in String localId) = 2;
+ void deleteCrash(in String localId) = 3;
+ void uploadCrash(in String localId) = 4;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterControllerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterControllerClient.aidl
new file mode 100644
index 00000000000..caf744e4eb4
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICrashReporterControllerClient.aidl
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import android.os.Bundle;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+interface ICrashReporterControllerClient {
+ void onPendingCrashReports(in String[] localIds) = 0;
+ void onCrashDeleted(in String localId) = 1;
+ void onCrashUploadSucceeded(in String localId, in String reportId) = 2;
+ void onCrashUploadFailed(in String localId, in String failureMessage) = 3;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl
new file mode 100644
index 00000000000..830f709b45f
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+/**
+ * Used to forward download requests to the client.
+ */
+interface IDownloadCallbackClient {
+ boolean interceptDownload(in String uriString, in String userAgent, in String contentDisposition, in String mimetype, long contentLength) = 0;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl
new file mode 100644
index 00000000000..1dfc22b1a2e
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+/**
+ * Allows the client to override the default way of handling user interactions
+ * with error pages (such as SSL interstitials).
+ */
+interface IErrorPageCallbackClient {
+ boolean onBackToSafety() = 0;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl
new file mode 100644
index 00000000000..fff55334c24
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+/**
+ * Used to forward FullscreenCallback calls to the client.
+ */
+interface IFullscreenCallbackClient {
+ // exitFullscreenWrapper is a ValueCallback<Void> that when run exits
+ // fullscreen.
+ void enterFullscreen(in IObjectWrapper exitFullscreenWrapper) = 0;
+ void exitFullscreen() = 1;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigation.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
index b0194de42dc..7a7a8969502 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigation.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
/**
* Provides information about a navigation.
@@ -13,4 +13,12 @@ interface INavigation {
String getUri() = 1;
List<String> getRedirectChain() = 2;
+
+ int getHttpStatusCode() = 3;
+
+ boolean isSameDocument() = 4;
+
+ boolean isErrorPage() = 5;
+
+ int getLoadError() = 6;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
index fe744bfc227..08467c4bba1 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationController.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
interface INavigationController {
void navigate(in String uri) = 0;
@@ -20,4 +20,8 @@ interface INavigationController {
int getNavigationListCurrentIndex() = 6;
String getNavigationEntryDisplayUri(in int index) = 7;
+
+ boolean canGoBack() = 8;
+
+ boolean canGoForward() = 9;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl
index d5571424afc..60132f87c71 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IClientNavigation;
+import org.chromium.weblayer_private.interfaces.INavigation;
/**
* Interface used by NavigationController to inform the client of changes. This largely duplicates
- * the NavigationObserver interface, but is a singleton to avoid unnecessary IPC.
+ * the NavigationCallback interface, but is a singleton to avoid unnecessary IPC.
*/
interface INavigationControllerClient {
IClientNavigation createClientNavigation(in INavigation impl) = 0;
@@ -15,9 +18,15 @@ interface INavigationControllerClient {
void navigationRedirected(IClientNavigation navigation) = 2;
- void navigationCommitted(IClientNavigation navigation) = 3;
+ void readyToCommitNavigation(IClientNavigation navigation) = 3;
void navigationCompleted(IClientNavigation navigation) = 4;
void navigationFailed(IClientNavigation navigation) = 5;
+
+ void loadStateChanged(boolean isLoading, boolean toDifferentDocument) = 6;
+
+ void loadProgressChanged(double progress) = 7;
+
+ void onFirstContentfulPaint() = 8;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IObjectWrapper.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IObjectWrapper.aidl
index 34f8d76df5b..91746ddce47 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/IObjectWrapper.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IObjectWrapper.aidl
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
/**
* This interface intentionally has no methods, and instances of this should
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
new file mode 100644
index 00000000000..ef92f749a96
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+interface IProfile {
+ void destroy() = 0;
+
+ void clearBrowsingData(in int[] dataTypes, long fromMillis, long toMillis,
+ in IObjectWrapper completionCallback) = 1;
+
+ String getName() = 2;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl
new file mode 100644
index 00000000000..65b22e106e6
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragment.aidl
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+interface IRemoteFragment {
+ // Using IObjectWrapper instead of Bundle to pass by reference instead of by value.
+ void handleOnCreate(in IObjectWrapper savedInstanceState) = 0;
+ void handleOnAttach(in IObjectWrapper context) = 1;
+ void handleOnActivityCreated(in IObjectWrapper savedInstanceState) = 2;
+ IObjectWrapper handleOnCreateView() = 3;
+ void handleOnStart() = 4;
+ void handleOnResume() = 5;
+ void handleOnPause() = 6;
+ void handleOnStop() = 7;
+ void handleOnDestroyView() = 8;
+ void handleOnDetach() = 9;
+ void handleOnDestroy() = 10;
+ void handleOnSaveInstanceState(in IObjectWrapper outState) = 11;
+ // |data| is an Intent with the result returned from the activity.
+ void handleOnActivityResult(int requestCode,
+ int resultCode,
+ in IObjectWrapper data) = 12;
+ void handleOnRequestPermissionsResult(int requestCode,
+ in String[] permissions,
+ in int[] grantResults) = 13;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl
new file mode 100644
index 00000000000..34f41b90c39
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IRemoteFragmentClient.aidl
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+
+interface IRemoteFragmentClient {
+ void superOnCreate(in IObjectWrapper savedInstanceState) = 0;
+ void superOnAttach(in IObjectWrapper context) = 1;
+ void superOnActivityCreated(in IObjectWrapper savedInstanceState) = 2;
+ void superOnStart() = 3;
+ void superOnResume() = 4;
+ void superOnPause() = 5;
+ void superOnStop() = 6;
+ void superOnDestroyView() = 7;
+ void superOnDetach() = 8;
+ void superOnDestroy() = 9;
+ void superOnSaveInstanceState(in IObjectWrapper outState) = 10;
+
+ IObjectWrapper getActivity() = 11;
+ boolean startActivityForResult(in IObjectWrapper intent,
+ int requestCode,
+ in IObjectWrapper options) = 12;
+ boolean startIntentSenderForResult(in IObjectWrapper intent,
+ int requestCode,
+ in IObjectWrapper fillInIntent,
+ int flagsMask,
+ int flagsValues,
+ int extraFlags,
+ in IObjectWrapper options) = 13;
+ boolean shouldShowRequestPermissionRationale(String permission) = 14;
+ void requestPermissions(in String[] permissions, int requestCode) = 15;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
new file mode 100644
index 00000000000..b153adcad09
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
+import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
+import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
+import org.chromium.weblayer_private.interfaces.INavigationController;
+import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ITabClient;
+
+interface ITab {
+ void setClient(in ITabClient client) = 0;
+
+ INavigationController createNavigationController(in INavigationControllerClient client) = 1;
+
+ void setDownloadCallbackClient(IDownloadCallbackClient client) = 2;
+
+ void setErrorPageCallbackClient(IErrorPageCallbackClient client) = 3;
+
+ void setFullscreenCallbackClient(in IFullscreenCallbackClient client) = 4;
+
+ void executeScript(in String script, boolean useSeparateIsolate, in IObjectWrapper callback) = 5;
+
+ void setNewTabsEnabled(in boolean enabled) = 6;
+
+ // Returns a unique identifier for this Tab. The id is *not* unique across
+ // restores. The id is intended for the client library to avoid creating duplicate client objects
+ // for the same ITab.
+ int getId() = 7;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
new file mode 100644
index 00000000000..92bdac6c5e8
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+/**
+ * Interface used by Tab to inform the client of changes. This largely duplicates the
+ * TabCallback interface, but is a singleton to avoid unnecessary IPC.
+ */
+interface ITabClient {
+ void visibleUriChanged(in String uriString) = 0;
+
+ void onNewTab(in int tabId, in int mode) = 1;
+
+ void onRenderProcessGone() = 2;
+
+ void onCloseTab() = 3;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl
new file mode 100644
index 00000000000..7e4be7f0c53
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import android.os.Bundle;
+
+import org.chromium.weblayer_private.interfaces.IBrowserFragment;
+import org.chromium.weblayer_private.interfaces.ICrashReporterController;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.IProfile;
+import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
+
+interface IWebLayer {
+ // Initializes WebLayer and starts loading.
+ //
+ // It is expected that either loadAsync or loadSync is called before anything else.
+ //
+ // @param appContext A Context that refers to the Application using WebLayer.
+ // @param loadedCallback A ValueCallback that will be called when load completes.
+ void loadAsync(in IObjectWrapper appContext,
+ in IObjectWrapper loadedCallback) = 1;
+
+ // Initializes WebLayer, starts loading and blocks until loading has completed.
+ //
+ // It is expected that either loadAsync or loadSync is called before anything else.
+ //
+ // @param appContext A Context that refers to the Application using WebLayer.
+ void loadSync(in IObjectWrapper appContext) = 2;
+
+ // Creates the WebLayer counterpart to a BrowserFragment - a BrowserFragmentImpl
+ //
+ // @param fragmentClient Representative of the Fragment on the client side through which
+ // WebLayer can call methods on Fragment.
+ // @param fragmentArgs Bundle of arguments with which the Fragment was created on the client side
+ // (see Fragment#setArguments).
+ IBrowserFragment createBrowserFragmentImpl(in IRemoteFragmentClient fragmentClient,
+ in IObjectWrapper fragmentArgs) = 3;
+
+ // Create or get the profile matching profileName.
+ IProfile getProfile(in String profileName) = 4;
+
+ // Enable or disable DevTools remote debugging server.
+ void setRemoteDebuggingEnabled(boolean enabled) = 5;
+
+ // Returns whether or not the DevTools remote debugging server is enabled.
+ boolean isRemoteDebuggingEnabled() = 6;
+
+ // Returns the singleton crash reporter controller. If WebLayer has not been
+ // initialized, does the minimum initialization needed for the crash reporter.
+ ICrashReporterController getCrashReporterController(
+ in IObjectWrapper appContext) = 7;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl
new file mode 100644
index 00000000000..214a966394a
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import android.os.Bundle;
+
+import org.chromium.weblayer_private.interfaces.IWebLayer;
+
+// Factory for creating IWebLayer as well as determining if a particular version
+// of a client is supported.
+interface IWebLayerFactory {
+ // Returns true if a client with the specified version is supported.
+ boolean isClientSupported() = 0;
+
+ // Creates a new IWebLayer. It is expected that a client has a single
+ // IWebLayer. Further, at this time, only a single client is supported.
+ IWebLayer createWebLayer() = 1;
+
+ // Returns the full version string of the implementation.
+ String getImplementationVersion() = 2;
+
+ // Returns the major version of the implementation.
+ int getImplementationMajorVersion() = 3;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java
new file mode 100644
index 00000000000..eba5fbf0c51
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({LoadError.NO_ERROR, LoadError.HTTP_CLIENT_ERROR, LoadError.HTTP_SERVER_ERROR,
+ LoadError.SSL_ERROR, LoadError.CONNECTIVITY_ERROR, LoadError.OTHER_ERROR})
+@Retention(RetentionPolicy.SOURCE)
+public @interface LoadError {
+ int NO_ERROR = 0;
+ int HTTP_CLIENT_ERROR = 1;
+ int HTTP_SERVER_ERROR = 2;
+ int SSL_ERROR = 3;
+ int CONNECTIVITY_ERROR = 4;
+ int OTHER_ERROR = 5;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigationState.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigationState.java
new file mode 100644
index 00000000000..d0c0f608a8a
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigationState.java
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({NavigationState.WAITING_RESPONSE, NavigationState.RECEIVING_BYTES,
+ NavigationState.COMPLETE, NavigationState.FAILED})
+@Retention(RetentionPolicy.SOURCE)
+public @interface NavigationState {
+ int WAITING_RESPONSE = 0;
+ int RECEIVING_BYTES = 1;
+ int COMPLETE = 2;
+ int FAILED = 3;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NewTabType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NewTabType.java
new file mode 100644
index 00000000000..dbd83525e84
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NewTabType.java
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({NewTabType.FOREGROUND_TAB, NewTabType.BACKGROUND_TAB, NewTabType.NEW_POPUP,
+ NewTabType.NEW_WINDOW})
+@Retention(RetentionPolicy.SOURCE)
+public @interface NewTabType {
+ int FOREGROUND_TAB = 0;
+ int BACKGROUND_TAB = 1;
+ int NEW_POPUP = 2;
+ int NEW_WINDOW = 3;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/ObjectWrapper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ObjectWrapper.java
index 2a510722d75..41beb409765 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/aidl/ObjectWrapper.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ObjectWrapper.java
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package org.chromium.weblayer_private.aidl;
+package org.chromium.weblayer_private.interfaces;
import android.os.IBinder;
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
new file mode 100644
index 00000000000..381431a1019
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.interfaces;
+
+/**
+ * Holds the current version number of WebLayer.
+ *
+ * Whenever any AIDL file is changed, sVersionNumber must be incremented.
+ * */
+public final class WebLayerVersion { public static final int sVersionNumber = 20; }