summaryrefslogtreecommitdiff
path: root/chromium/weblayer/browser/java
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/weblayer/browser/java
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-c30a6232df03e1efbd9f3b226777b07e087a1122.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/weblayer/browser/java')
-rw-r--r--chromium/weblayer/browser/java/BUILD.gn90
-rw-r--r--chromium/weblayer/browser/java/DEPS3
-rw-r--r--chromium/weblayer/browser/java/ResourceId.template1
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/AccessibilityUtil.java209
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/AutofillView.java3
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java60
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java4
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java40
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java33
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java81
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java519
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java57
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java1
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java9
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java17
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java331
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java238
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java486
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java852
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java257
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java69
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java44
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/IntentUtils.java48
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java140
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java23
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java28
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java14
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java6
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java40
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java42
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/README.md36
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java39
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java421
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java189
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java578
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java75
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java321
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java278
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java63
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java240
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java14
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerAccessibilityUtil.java2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java9
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java41
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java84
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java7
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java42
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java76
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/WebShareServiceFactory.java40
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl3
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl4
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl6
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl15
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl7
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl4
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl2
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageCallbackClient.aidl16
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageReplyProxy.aidl9
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ScrollNotificationType.java18
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java7
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java19
-rw-r--r--chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl25
-rw-r--r--chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml16
-rw-r--r--chromium/weblayer/browser/java/res/drawable/weblayer_tab_indicator.xml15
-rw-r--r--chromium/weblayer/browser/java/res/layout/site_settings_layout.xml13
-rw-r--r--chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml39
-rw-r--r--chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml28
-rw-r--r--chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml21
-rw-r--r--chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml48
-rw-r--r--chromium/weblayer/browser/java/res/layout/weblayer_url_bar.xml2
-rw-r--r--chromium/weblayer/browser/java/res/values/colors.xml11
-rw-r--r--chromium/weblayer/browser/java/res/values/dimens.xml11
-rw-r--r--chromium/weblayer/browser/java/res/values/styles.xml43
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb9
-rw-r--r--chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb9
-rw-r--r--chromium/weblayer/browser/java/weblayer_strings.grd28
155 files changed, 6526 insertions, 906 deletions
diff --git a/chromium/weblayer/browser/java/BUILD.gn b/chromium/weblayer/browser/java/BUILD.gn
index e5a00fb6c61..38cdda128ce 100644
--- a/chromium/weblayer/browser/java/BUILD.gn
+++ b/chromium/weblayer/browser/java/BUILD.gn
@@ -9,20 +9,33 @@ import("//weblayer/variables.gni")
android_resources("weblayer_resources") {
sources = [
+ "res/drawable/weblayer_infobar_wrapper_bg.xml",
+ "res/drawable/weblayer_tab_indicator.xml",
"res/layout/site_settings_layout.xml",
+ "res/layout/weblayer_infobar_translate_compact_content.xml",
+ "res/layout/weblayer_infobar_translate_tab_content.xml",
+ "res/layout/weblayer_translate_menu_item.xml",
+ "res/layout/weblayer_translate_menu_item_checked.xml",
"res/layout/weblayer_url_bar.xml",
+ "res/values/colors.xml",
"res/values/dimens.xml",
"res/values/styles.xml",
]
custom_package = "org.chromium.weblayer_private"
deps = [
":weblayer_strings_grd",
+ "//components/blocked_content/android:java_resources",
+ "//components/browser_ui/http_auth/android:java_resources",
+ "//components/browser_ui/media/android:java_resources",
"//components/browser_ui/settings/android:java_resources",
"//components/browser_ui/site_settings/android:java_resources",
"//components/browser_ui/strings/android:browser_ui_strings_grd",
"//components/browser_ui/styles/android:java_resources",
+ "//components/infobars/android:java_resources",
"//components/page_info/android:java_resources",
"//components/permissions/android:java_resources",
+ "//components/translate/content/android:java_resources",
+ "//third_party/android_deps:com_google_android_material_material_java",
"//weblayer:components_java_strings",
]
}
@@ -35,6 +48,7 @@ java_cpp_template("resource_id_javagen") {
sources = [ "ResourceId.template" ]
package_path = "org/chromium/weblayer_private/resources"
inputs = [
+ "//components/resources/android/blocked_content_resource_id.h",
"//components/resources/android/page_info_resource_id.h",
"//components/resources/android/permissions_resource_id.h",
]
@@ -51,6 +65,8 @@ java_strings_grd("weblayer_strings_grd") {
java_cpp_enum("generated_enums") {
sources = [
"//weblayer/browser/controls_visibility_reason.h",
+ "//weblayer/browser/infobar_android.h",
+ "//weblayer/browser/translate_utils.h",
"//weblayer/public/download.h",
"//weblayer/public/navigation.h",
"//weblayer/public/new_tab_delegate.h",
@@ -60,7 +76,6 @@ java_cpp_enum("generated_enums") {
android_library("java") {
sources = [
- "org/chromium/weblayer_private/AccessibilityUtil.java",
"org/chromium/weblayer_private/ActionModeCallback.java",
"org/chromium/weblayer_private/AutocompleteSchemeClassifierImpl.java",
"org/chromium/weblayer_private/AutofillView.java",
@@ -69,7 +84,7 @@ android_library("java") {
"org/chromium/weblayer_private/BrowserImpl.java",
"org/chromium/weblayer_private/BrowserViewController.java",
"org/chromium/weblayer_private/ChildProcessServiceImpl.java",
- "org/chromium/weblayer_private/ContentView.java",
+ "org/chromium/weblayer_private/ConfirmInfoBar.java",
"org/chromium/weblayer_private/ContentViewRenderView.java",
"org/chromium/weblayer_private/CookieManagerImpl.java",
"org/chromium/weblayer_private/CrashReporterControllerImpl.java",
@@ -80,9 +95,19 @@ android_library("java") {
"org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java",
"org/chromium/weblayer_private/FragmentWindowAndroid.java",
"org/chromium/weblayer_private/FullscreenCallbackProxy.java",
+ "org/chromium/weblayer_private/InfoBar.java",
+ "org/chromium/weblayer_private/InfoBarCompactLayout.java",
+ "org/chromium/weblayer_private/InfoBarContainer.java",
+ "org/chromium/weblayer_private/InfoBarContainerLayout.java",
+ "org/chromium/weblayer_private/InfoBarContainerView.java",
+ "org/chromium/weblayer_private/InfoBarUiItem.java",
+ "org/chromium/weblayer_private/InfoBarWrapper.java",
+ "org/chromium/weblayer_private/IntentUtils.java",
"org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java",
"org/chromium/weblayer_private/LocaleChangedBroadcastReceiver.java",
+ "org/chromium/weblayer_private/MediaSessionManager.java",
"org/chromium/weblayer_private/MediaStreamManager.java",
+ "org/chromium/weblayer_private/MojoInterfaceRegistrar.java",
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
"org/chromium/weblayer_private/NewTabCallbackProxy.java",
@@ -91,8 +116,15 @@ android_library("java") {
"org/chromium/weblayer_private/ProfileManager.java",
"org/chromium/weblayer_private/RemoteFragmentImpl.java",
"org/chromium/weblayer_private/SiteSettingsFragmentImpl.java",
+ "org/chromium/weblayer_private/SwipableOverlayView.java",
"org/chromium/weblayer_private/TabCallbackProxy.java",
"org/chromium/weblayer_private/TabImpl.java",
+ "org/chromium/weblayer_private/TranslateCompactInfoBar.java",
+ "org/chromium/weblayer_private/TranslateMenu.java",
+ "org/chromium/weblayer_private/TranslateMenuHelper.java",
+ "org/chromium/weblayer_private/TranslateOptions.java",
+ "org/chromium/weblayer_private/TranslateTabContent.java",
+ "org/chromium/weblayer_private/TranslateTabLayout.java",
"org/chromium/weblayer_private/UrlBarControllerImpl.java",
"org/chromium/weblayer_private/WebContentsGestureStateTracker.java",
"org/chromium/weblayer_private/WebLayerAccessibilityUtil.java",
@@ -103,6 +135,8 @@ android_library("java") {
"org/chromium/weblayer_private/WebLayerNotificationChannels.java",
"org/chromium/weblayer_private/WebLayerSiteSettingsClient.java",
"org/chromium/weblayer_private/WebLayerTabModalPresenter.java",
+ "org/chromium/weblayer_private/WebMessageReplyProxyImpl.java",
+ "org/chromium/weblayer_private/WebShareServiceFactory.java",
"org/chromium/weblayer_private/WebViewCompatibilityHelperImpl.java",
"org/chromium/weblayer_private/metrics/MetricsServiceClient.java",
"org/chromium/weblayer_private/metrics/UmaUtils.java",
@@ -116,26 +150,34 @@ android_library("java") {
":weblayer_resources",
"//base:base_java",
"//base:jni_java",
- "//components/autofill/android:provider_java",
+ "//components/autofill/android/provider:java",
+ "//components/browser_ui/client_certificate/android:java",
+ "//components/browser_ui/http_auth/android:java",
+ "//components/browser_ui/media/android:java",
"//components/browser_ui/modaldialog/android:java",
"//components/browser_ui/notifications/android:java",
"//components/browser_ui/settings/android:java",
+ "//components/browser_ui/share/android:java",
"//components/browser_ui/site_settings/android:java",
"//components/browser_ui/styles/android:java",
- "//components/browser_ui/styles/android:java_resources",
"//components/browser_ui/util/android:java",
+ "//components/browser_ui/webshare/android:java",
+ "//components/browser_ui/widget/android:java",
"//components/content_settings/android:java",
"//components/crash/android:handler_java",
"//components/crash/android:java",
"//components/download/internal/common:internal_java",
"//components/embedder_support/android:application_java",
"//components/embedder_support/android:browser_context_java",
+ "//components/embedder_support/android:content_view_java",
"//components/embedder_support/android:context_menu_java",
"//components/embedder_support/android:util_java",
"//components/embedder_support/android:web_contents_delegate_java",
"//components/embedder_support/android/metrics:java",
"//components/external_intents/android:java",
"//components/find_in_page/android:java",
+ "//components/infobars/android:java",
+ "//components/infobars/core:infobar_enums_java",
"//components/javascript_dialogs/android:java",
"//components/location/android:settings_java",
"//components/metrics:metrics_java",
@@ -150,11 +192,22 @@ android_library("java") {
"//components/url_formatter/android:url_formatter_java",
"//components/variations/android:variations_java",
"//components/version_info/android:version_constants_java",
+ "//components/webapk/android/libs/client:java",
+ "//components/webapk/android/libs/common:java",
"//components/webrtc/android:java",
"//content/public/android:content_java",
+ "//mojo/public/java:bindings_java",
"//net/android:net_java",
+ "//services/network/public/mojom:cookies_mojom_java",
"//services/network/public/mojom:mojom_java",
+ "//services/service_manager/public/java:service_manager_java",
+ "//third_party/android_deps:androidx_appcompat_appcompat_java",
+ "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
"//third_party/android_deps:androidx_core_core_java",
+ "//third_party/android_deps:androidx_fragment_fragment_java",
+ "//third_party/android_deps:androidx_preference_preference_java",
+ "//third_party/android_deps:com_google_android_material_material_java",
+ "//third_party/blink/public/mojom:android_mojo_bindings_java",
"//ui/android:ui_full_java",
"//ui/android:ui_java",
"//url:gurl_java",
@@ -188,9 +241,16 @@ android_resources("weblayer_test_resources") {
android_library("test_java") {
testonly = true
- sources = [ "org/chromium/weblayer_private/test/TestWebLayerImpl.java" ]
+ sources = [
+ "org/chromium/weblayer_private/test/TestInfoBar.java",
+ "org/chromium/weblayer_private/test/TestWebLayerImpl.java",
+ ]
deps = [
+ ":interfaces_java",
+ ":java",
":weblayer_test_resources",
+ "//base:jni_java",
+ "//components/location/android:location_java",
"//components/permissions/android:java",
"//content/public/test/android:content_java_test_support",
"//net/android:net_java",
@@ -199,6 +259,15 @@ android_library("test_java") {
"//ui/android:ui_full_java",
]
srcjar_deps = [ ":test_aidl" ]
+ annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("test_jni") {
+ testonly = true
+ sources = [
+ "org/chromium/weblayer_private/test/TestInfoBar.java",
+ "org/chromium/weblayer_private/test/TestWebLayerImpl.java",
+ ]
}
generate_jni("jni") {
@@ -206,25 +275,30 @@ generate_jni("jni") {
"org/chromium/weblayer_private/AutocompleteSchemeClassifierImpl.java",
"org/chromium/weblayer_private/BrowserControlsContainerView.java",
"org/chromium/weblayer_private/BrowserImpl.java",
+ "org/chromium/weblayer_private/ConfirmInfoBar.java",
"org/chromium/weblayer_private/ContentViewRenderView.java",
"org/chromium/weblayer_private/CookieManagerImpl.java",
"org/chromium/weblayer_private/DownloadCallbackProxy.java",
"org/chromium/weblayer_private/DownloadImpl.java",
"org/chromium/weblayer_private/ErrorPageCallbackProxy.java",
"org/chromium/weblayer_private/FullscreenCallbackProxy.java",
+ "org/chromium/weblayer_private/InfoBar.java",
+ "org/chromium/weblayer_private/InfoBarContainer.java",
"org/chromium/weblayer_private/LocaleChangedBroadcastReceiver.java",
"org/chromium/weblayer_private/MediaStreamManager.java",
+ "org/chromium/weblayer_private/MojoInterfaceRegistrar.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/TranslateCompactInfoBar.java",
"org/chromium/weblayer_private/UrlBarControllerImpl.java",
"org/chromium/weblayer_private/WebLayerExceptionFilter.java",
"org/chromium/weblayer_private/WebLayerFactoryImpl.java",
"org/chromium/weblayer_private/WebLayerImpl.java",
- "org/chromium/weblayer_private/WebLayerSiteSettingsClient.java",
+ "org/chromium/weblayer_private/WebMessageReplyProxyImpl.java",
"org/chromium/weblayer_private/WebViewCompatibilityHelperImpl.java",
"org/chromium/weblayer_private/metrics/MetricsServiceClient.java",
"org/chromium/weblayer_private/metrics/UmaUtils.java",
@@ -246,11 +320,13 @@ android_library("interfaces_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/ScrollNotificationType.java",
"org/chromium/weblayer_private/interfaces/SettingType.java",
"org/chromium/weblayer_private/interfaces/SiteSettingsFragmentArgs.java",
"org/chromium/weblayer_private/interfaces/SiteSettingsIntentHelper.java",
"org/chromium/weblayer_private/interfaces/StrictModeWorkaround.java",
"org/chromium/weblayer_private/interfaces/UrlBarOptionsKeys.java",
+ "org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java",
]
deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
@@ -316,6 +392,8 @@ android_aidl("aidl") {
"org/chromium/weblayer_private/interfaces/IWebLayer.aidl",
"org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl",
"org/chromium/weblayer_private/interfaces/IWebLayerFactory.aidl",
+ "org/chromium/weblayer_private/interfaces/IWebMessageCallbackClient.aidl",
+ "org/chromium/weblayer_private/interfaces/IWebMessageReplyProxy.aidl",
]
}
diff --git a/chromium/weblayer/browser/java/DEPS b/chromium/weblayer/browser/java/DEPS
index 5218c75b075..819b5a6c915 100644
--- a/chromium/weblayer/browser/java/DEPS
+++ b/chromium/weblayer/browser/java/DEPS
@@ -1,11 +1,14 @@
include_rules = [
+ "+components/browser_ui/http_auth",
"+components/browser_ui/util/android",
"+components/content_settings/android/java",
"+components/crash/android/java",
"+components/external_intents",
+ "+components/infobars/android",
"+components/location/android/java/src/org/chromium/components/location",
"+components/minidump_uploader",
"+components/page_info/android/java",
+ "+components/webapk/android/libs",
"+services/device/public/java/src/org/chromium/device/geolocation",
# WebLayerNotificationBuilder should be used for all notifications.
diff --git a/chromium/weblayer/browser/java/ResourceId.template b/chromium/weblayer/browser/java/ResourceId.template
index 0f5352d7e40..1e16c782f49 100644
--- a/chromium/weblayer/browser/java/ResourceId.template
+++ b/chromium/weblayer/browser/java/ResourceId.template
@@ -11,6 +11,7 @@ class ResourceId {
int[] resourceList = {
#define LINK_RESOURCE_ID(c_id,java_id) java_id,
#define DECLARE_RESOURCE_ID(c_id,java_id) java_id,
+#include "components/resources/android/blocked_content_resource_id.h"
#include "components/resources/android/page_info_resource_id.h"
#include "components/resources/android/permissions_resource_id.h"
};
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/AccessibilityUtil.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/AccessibilityUtil.java
deleted file mode 100644
index 5382c726ad7..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/AccessibilityUtil.java
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(sky): this is a forked copy of that from src/chrome, refactor and share.
-
-package org.chromium.weblayer_private;
-
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
-import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.ObserverList;
-import org.chromium.base.TraceEvent;
-import org.chromium.base.task.PostTask;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
-
-import java.util.List;
-
-/**
- * Exposes information about the current accessibility state.
- */
-public class AccessibilityUtil {
- /**
- * An observer to be notified of accessibility status changes.
- */
- public interface Observer {
- /**
- * @param enabled Whether a touch exploration or an accessibility service that performs can
- * perform gestures is enabled. Indicates that the UI must be fully navigable using
- * the accessibility view tree.
- */
- void onAccessibilityModeChanged(boolean enabled);
- }
-
- private Boolean mIsAccessibilityEnabled;
- private ObserverList<Observer> mObservers;
- private final class ModeChangeHandler
- implements AccessibilityStateChangeListener, TouchExplorationStateChangeListener {
- // AccessibilityStateChangeListener
-
- @Override
- public final void onAccessibilityStateChanged(boolean enabled) {
- updateIsAccessibilityEnabledAndNotify();
- }
-
- // TouchExplorationStateChangeListener
-
- @Override
- public void onTouchExplorationStateChanged(boolean enabled) {
- updateIsAccessibilityEnabledAndNotify();
- }
- }
-
- private ModeChangeHandler mModeChangeHandler;
-
- protected AccessibilityUtil() {}
-
- /**
- * Checks to see that this device has accessibility and touch exploration enabled.
- * @return Whether or not accessibility and touch exploration are enabled.
- */
- public boolean isAccessibilityEnabled() {
- if (mModeChangeHandler == null) registerModeChangeListeners();
- if (mIsAccessibilityEnabled != null) return mIsAccessibilityEnabled;
-
- TraceEvent.begin("AccessibilityManager::isAccessibilityEnabled");
-
- AccessibilityManager manager = getAccessibilityManager();
- boolean accessibilityEnabled =
- manager != null && manager.isEnabled() && manager.isTouchExplorationEnabled();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && manager != null
- && manager.isEnabled() && !accessibilityEnabled) {
- List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- for (AccessibilityServiceInfo service : services) {
- if (canPerformGestures(service)) {
- accessibilityEnabled = true;
- break;
- }
- }
- }
-
- mIsAccessibilityEnabled = accessibilityEnabled;
-
- TraceEvent.end("AccessibilityManager::isAccessibilityEnabled");
- return mIsAccessibilityEnabled;
- }
-
- /**
- * Add {@link Observer} object. The observer will be notified of the current accessibility
- * mode immediately.
- * @param observer Observer object monitoring a11y mode change.
- */
- public void addObserver(Observer observer) {
- getObservers().addObserver(observer);
-
- // Notify mode change to a new observer so things are initialized correctly when Chrome
- // has been re-started after closing due to the last tab being closed when homepage is
- // enabled. See crbug.com/541546.
- observer.onAccessibilityModeChanged(isAccessibilityEnabled());
- }
-
- /**
- * Remove {@link Observer} object.
- * @param observer Observer object monitoring a11y mode change.
- */
- public void removeObserver(Observer observer) {
- getObservers().removeObserver(observer);
- }
-
- /**
- * @return True if a hardware keyboard is detected.
- */
- public static boolean isHardwareKeyboardAttached(Configuration c) {
- return c.keyboard != Configuration.KEYBOARD_NOKEYS;
- }
-
- private AccessibilityManager getAccessibilityManager() {
- return (AccessibilityManager) ContextUtils.getApplicationContext().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
-
- private void registerModeChangeListeners() {
- assert mModeChangeHandler == null;
- mModeChangeHandler = new ModeChangeHandler();
- AccessibilityManager manager = getAccessibilityManager();
- manager.addAccessibilityStateChangeListener(mModeChangeHandler);
- manager.addTouchExplorationStateChangeListener(mModeChangeHandler);
- }
-
- /**
- * Removes all global state tracking observers/listeners as well as any observers added to this.
- * As this removes all observers, be very careful in calling. In general, only call when the
- * application is going to be destroyed.
- */
- protected void stopTrackingStateAndRemoveObservers() {
- if (mObservers != null) mObservers.clear();
- if (mModeChangeHandler == null) return;
- AccessibilityManager manager = getAccessibilityManager();
- manager.removeAccessibilityStateChangeListener(mModeChangeHandler);
- manager.removeTouchExplorationStateChangeListener(mModeChangeHandler);
- }
-
- /**
- * Forces recalculating the value of isAccessibilityEnabled(). If the value has changed observer
- * are notified.
- */
- protected void updateIsAccessibilityEnabledAndNotify() {
- boolean oldIsAccessibilityEnabled = isAccessibilityEnabled();
- // Setting to null forces the next call to isAccessibilityEnabled() to update the value.
- mIsAccessibilityEnabled = null;
- if (oldIsAccessibilityEnabled != isAccessibilityEnabled()) notifyModeChange();
- }
-
- private ObserverList<Observer> getObservers() {
- if (mObservers == null) mObservers = new ObserverList<>();
- return mObservers;
- }
-
- /**
- * Notify all the observers of the mode change.
- */
- private void notifyModeChange() {
- boolean enabled = isAccessibilityEnabled();
- for (Observer observer : getObservers()) {
- observer.onAccessibilityModeChanged(enabled);
- }
- }
-
- /**
- * Checks whether the given {@link AccessibilityServiceInfo} can perform gestures.
- * @param service The service to check.
- * @return Whether the {@code service} can perform gestures. On N+, this relies on the
- * capabilities the service can perform. On L & M, this looks specifically for
- * Switch Access.
- */
- private boolean canPerformGestures(AccessibilityServiceInfo service) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return (service.getCapabilities()
- & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES)
- != 0;
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- return service.getResolveInfo() != null
- && service.getResolveInfo().toString().contains("switchaccess");
- }
- return false;
- }
-
- /**
- * Set whether the device has accessibility enabled. Should be reset back to null after the test
- * has finished.
- * @param isEnabled whether the device has accessibility enabled.
- */
- @VisibleForTesting
- public void setAccessibilityEnabledForTesting(@Nullable Boolean isEnabled) {
- mIsAccessibilityEnabled = isEnabled;
- PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, this::notifyModeChange);
- }
-}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/AutofillView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/AutofillView.java
index 1b2992d55b4..ca8217eae14 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/AutofillView.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/AutofillView.java
@@ -12,9 +12,12 @@ import android.view.ViewStructure;
import android.view.autofill.AutofillValue;
import android.widget.FrameLayout;
+import org.chromium.base.annotations.VerifiesOnO;
+
/**
* View which handles autofill support for a tab.
*/
+@VerifiesOnO
public class AutofillView extends FrameLayout {
private TabImpl mTab;
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
index d860d7cdef4..ce10368c02c 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
@@ -267,15 +267,32 @@ class BrowserControlsContainerView extends FrameLayout {
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();
+ boolean heightChanged = height != mLastHeight;
+ if (!heightChanged && width == mLastWidth) return;
+
+ mLastWidth = width;
+ mLastHeight = height;
+ if (mLastWidth > 0 && mLastHeight > 0 && mViewResourceAdapter == null) {
+ createAdapterAndLayer();
+ } else if (mViewResourceAdapter != null) {
+ BrowserControlsContainerViewJni.get().setControlsSize(
+ mNativeBrowserControlsContainerView, mLastWidth, mLastHeight);
+ if (mWebContents != null) mWebContents.notifyBrowserControlsHeightChanged();
+ if (heightChanged) {
+ // When the height changes cc doesn't generate a new frame, which means this code
+ // must process the change now. If cc generated a new frame, it would likely be at
+ // the wrong size.
+ if (mControlsOffset == 0) {
+ // The controls are completely visible.
+ onOffsetsChanged(0, height);
} else {
- BrowserControlsContainerViewJni.get().setControlsSize(
- mNativeBrowserControlsContainerView, mLastWidth, mLastHeight);
+ // The controls are partially (and possibly completely) hidden. Snap to
+ // completely hidden.
+ if (mIsTop) {
+ onOffsetsChanged(-height, height);
+ } else {
+ onOffsetsChanged(height, 0);
+ }
}
}
}
@@ -333,7 +350,11 @@ class BrowserControlsContainerView extends FrameLayout {
private void finishScroll(int contentOffsetY) {
mInScroll = false;
setControlsOffset(0, contentOffsetY);
- mContentViewRenderView.postOnAnimation(() -> showControls());
+ if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) {
+ mContentViewRenderView.postOnAnimation(() -> showControls());
+ } else {
+ showControls();
+ }
}
private void setControlsOffset(int controlsOffsetY, int contentOffsetY) {
@@ -350,16 +371,20 @@ class BrowserControlsContainerView extends FrameLayout {
}
if (mIsTop) {
BrowserControlsContainerViewJni.get().setTopControlsOffset(
- mNativeBrowserControlsContainerView, mControlsOffset, mContentOffset);
+ mNativeBrowserControlsContainerView, mContentOffset);
} else {
BrowserControlsContainerViewJni.get().setBottomControlsOffset(
- mNativeBrowserControlsContainerView, mControlsOffset);
+ mNativeBrowserControlsContainerView);
}
}
private void prepareForScroll() {
mInScroll = true;
- mContentViewRenderView.postOnAnimation(() -> hideControls());
+ if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) {
+ mContentViewRenderView.postOnAnimation(() -> hideControls());
+ } else {
+ hideControls();
+ }
}
private void hideControls() {
@@ -371,6 +396,11 @@ class BrowserControlsContainerView extends FrameLayout {
}
@CalledByNative
+ private int getControlsOffset() {
+ return mControlsOffset;
+ }
+
+ @CalledByNative
private void didToggleFullscreenModeForTab(final boolean isFullscreen) {
// Delay hiding until after the animation. This comes from Chrome code.
if (mSystemUiFullscreenResizeRunnable != null) {
@@ -410,11 +440,11 @@ class BrowserControlsContainerView extends FrameLayout {
void deleteBrowserControlsContainerView(long nativeBrowserControlsContainerView);
void createControlsLayer(long nativeBrowserControlsContainerView, int id);
void deleteControlsLayer(long nativeBrowserControlsContainerView);
- void setTopControlsOffset(
- long nativeBrowserControlsContainerView, int controlsOffsetY, int contentOffsetY);
- void setBottomControlsOffset(long nativeBrowserControlsContainerView, int controlsOffsetY);
+ void setTopControlsOffset(long nativeBrowserControlsContainerView, int contentOffsetY);
+ void setBottomControlsOffset(long nativeBrowserControlsContainerView);
void setControlsSize(long nativeBrowserControlsContainerView, int width, int height);
void updateControlsResource(long nativeBrowserControlsContainerView);
void setWebContents(long nativeBrowserControlsContainerView, WebContents webContents);
+ boolean shouldDelayVisibilityChange();
}
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
index 33a8af8ce37..9ccb13e1704 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
@@ -4,6 +4,7 @@
package org.chromium.weblayer_private;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -127,7 +128,8 @@ public class BrowserFragmentImpl extends RemoteFragmentImpl {
@Override
public void onStop() {
super.onStop();
- mBrowser.onFragmentStop();
+ Activity activity = getActivity();
+ mBrowser.onFragmentStop(activity != null && activity.getChangingConfigurations() != 0);
}
@Override
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
index fc12eccb248..66433bde80a 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -63,6 +63,7 @@ public class BrowserImpl extends IBrowser.Stub {
private final UrlBarControllerImpl mUrlBarController;
private boolean mFragmentStarted;
private boolean mFragmentResumed;
+ private boolean mFragmentStoppedForConfigurationChange;
// Cache the value instead of querying system every time.
private Boolean mPasswordEchoEnabled;
private Boolean mDarkThemeEnabled;
@@ -106,6 +107,8 @@ public class BrowserImpl extends IBrowser.Stub {
? savedInstanceState.getByteArray(SAVED_STATE_MINIMAL_PERSISTENCE_STATE_KEY)
: null;
+ windowAndroid.restoreInstanceState(savedInstanceState);
+
createAttachmentState(embedderAppContext, windowAndroid);
mNativeBrowser = BrowserImplJni.get().createBrowser(profile.getNativeProfile(), this);
mUrlBarController = new UrlBarControllerImpl(this, mNativeBrowser);
@@ -160,6 +163,10 @@ public class BrowserImpl extends IBrowser.Stub {
outState.putByteArray(SAVED_STATE_MINIMAL_PERSISTENCE_STATE_KEY,
BrowserImplJni.get().getMinimalPersistenceState(mNativeBrowser));
}
+
+ if (mWindowAndroid != null) {
+ mWindowAndroid.saveInstanceState(outState);
+ }
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -188,6 +195,13 @@ public class BrowserImpl extends IBrowser.Stub {
}
@Override
+ public TabImpl createTab() {
+ TabImpl tab = new TabImpl(mProfile, mWindowAndroid);
+ addTab(tab);
+ return tab;
+ }
+
+ @Override
public void setSupportsEmbedding(boolean enable, IObjectWrapper valueCallback) {
StrictModeWorkaround.apply();
getViewController().setSupportsEmbedding(enable,
@@ -230,7 +244,7 @@ public class BrowserImpl extends IBrowser.Stub {
}
@CalledByNative
- private void createTabForSessionRestore(long nativeTab) {
+ private void createJavaTabForNativeTab(long nativeTab) {
new TabImpl(mProfile, mWindowAndroid, nativeTab);
}
@@ -309,7 +323,7 @@ public class BrowserImpl extends IBrowser.Stub {
@CalledByNative
private void onActiveTabChanged(TabImpl tab) {
- mViewController.setActiveTab(tab);
+ if (mViewController != null) mViewController.setActiveTab(tab);
if (mInDestroy) return;
try {
if (mClient != null) {
@@ -388,10 +402,8 @@ public class BrowserImpl extends IBrowser.Stub {
updateAllTabsAndSetActive();
} else if (persistenceInfo.mPersistenceId == null
|| persistenceInfo.mPersistenceId.isEmpty()) {
- TabImpl tab = new TabImpl(mProfile, mWindowAndroid);
- addTab(tab);
- boolean set_active_result = setActiveTab(tab);
- assert set_active_result;
+ boolean setActiveResult = setActiveTab(createTab());
+ assert setActiveResult;
} // else case is session restore, which will asynchronously create tabs.
}
@@ -404,7 +416,6 @@ public class BrowserImpl extends IBrowser.Stub {
}
private void destroyTabImpl(TabImpl tab) {
- BrowserImplJni.get().removeTab(mNativeBrowser, tab.getNativeTab());
tab.destroy();
}
@@ -438,24 +449,31 @@ public class BrowserImpl extends IBrowser.Stub {
}
public void onFragmentStart() {
+ mFragmentStoppedForConfigurationChange = false;
mFragmentStarted = true;
BrowserImplJni.get().onFragmentStart(mNativeBrowser);
updateAllTabs();
checkPreferences();
}
- public void onFragmentStop() {
+ public void onFragmentStop(boolean forConfigurationChange) {
+ mFragmentStoppedForConfigurationChange = forConfigurationChange;
mFragmentStarted = false;
+ if (mFragmentStoppedForConfigurationChange) {
+ destroyAttachmentState();
+ }
updateAllTabs();
}
public void onFragmentResume() {
mFragmentResumed = true;
WebLayerAccessibilityUtil.get().onBrowserResumed();
+ BrowserImplJni.get().onFragmentResume(mNativeBrowser);
}
public void onFragmentPause() {
mFragmentResumed = false;
+ BrowserImplJni.get().onFragmentPause(mNativeBrowser);
}
public boolean isStarted() {
@@ -466,6 +484,10 @@ public class BrowserImpl extends IBrowser.Stub {
return mFragmentResumed;
}
+ public boolean isFragmentStoppedForConfigurationChange() {
+ return mFragmentStoppedForConfigurationChange;
+ }
+
private void destroyAttachmentState() {
if (mLocaleReceiver != null) {
mLocaleReceiver.destroy();
@@ -515,5 +537,7 @@ public class BrowserImpl extends IBrowser.Stub {
byte[] persistenceCryptoKey, byte[] minimalPersistenceState);
void webPreferencesChanged(long nativeBrowserImpl);
void onFragmentStart(long nativeBrowserImpl);
+ void onFragmentResume(long nativeBrowserImpl);
+ void onFragmentPause(long nativeBrowserImpl);
}
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
index c9ae777bd63..436113adde3 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
@@ -17,6 +17,7 @@ import android.widget.RelativeLayout;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
+import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -79,7 +80,7 @@ public final class BrowserViewController
new BrowserControlsContainerView(context, mContentViewRenderView, this, false);
mBottomControlsContainerView.setId(View.generateViewId());
mContentView = ContentView.createContentView(
- context, mTopControlsContainerView.getEventOffsetHandler());
+ context, mTopControlsContainerView.getEventOffsetHandler(), null /* webContents */);
mContentViewRenderView.addView(mContentView,
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT));
@@ -125,6 +126,11 @@ public final class BrowserViewController
return mContentViewRenderView;
}
+ /** Returns the ViewGroup into which the InfoBarContainer should be parented. **/
+ public ViewGroup getInfoBarContainerParentView() {
+ return mContentViewRenderView;
+ }
+
public ViewGroup getContentView() {
return mContentView;
}
@@ -137,6 +143,14 @@ public final class BrowserViewController
return mAutofillView;
}
+ // Returns the index at which the infobar container view should be inserted.
+ public int getDesiredInfoBarContainerViewIndex() {
+ // Ensure that infobars are positioned behind WebContents overlays in z-order.
+ // TODO(blundell): Should infobars instead be hidden while a WebContents overlay is
+ // presented?
+ return mContentViewRenderView.indexOfChild(mWebContentsOverlayView) - 1;
+ }
+
public void setActiveTab(TabImpl tab) {
if (tab == mTab) return;
@@ -160,8 +174,8 @@ public final class BrowserViewController
new WebContentsGestureStateTracker(mContentView, webContents, this);
}
mAutofillView.setTab(mTab);
- mContentView.setTab(mTab);
+ mContentView.setWebContents(webContents);
mContentViewRenderView.setWebContents(webContents);
mTopControlsContainerView.setWebContents(webContents);
mBottomControlsContainerView.setWebContents(webContents);
@@ -184,6 +198,10 @@ public final class BrowserViewController
mBottomControlsContainerView.setView(view);
}
+ public int getBottomContentHeightDelta() {
+ return mBottomControlsContainerView.getContentHeightDelta();
+ }
+
public boolean compositorHasSurface() {
return mContentViewRenderView.hasSurface();
}
@@ -210,19 +228,24 @@ public final class BrowserViewController
}
@Override
- public void onDialogShown(PropertyModel model) {
+ public void onDialogAdded(PropertyModel model) {
onDialogVisibilityChanged(true);
}
@Override
- public void onDialogHidden(PropertyModel model) {
+ public void onLastDialogDismissed() {
onDialogVisibilityChanged(false);
}
private void onDialogVisibilityChanged(boolean showing) {
if (WebLayerFactoryImpl.getClientMajorVersion() < 82) return;
- if (mModalDialogManager.getCurrentType() == ModalDialogType.TAB) {
+ // ModalDialogManager.onLastDialogDismissed() may be called if |mTab| is currently null.
+ // This is because in some situations ModalDialogManager calls onLastDialogDismissed() even
+ // if there were no dialogs present and dismissDialog() is called. This matters as
+ // dismissDialog() may be called when |mTab| is null.
+ // TODO(sky): fix ModalDialogManager and remove mTab conditional.
+ if (mModalDialogManager.getCurrentType() == ModalDialogType.TAB && mTab != null) {
try {
mTab.getClient().onTabModalStateChanged(showing);
} catch (RemoteException e) {
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java
new file mode 100644
index 00000000000..4b49a4e8ebf
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java
@@ -0,0 +1,81 @@
+// Copyright 2020 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.graphics.Bitmap;
+
+import androidx.annotation.ColorRes;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.components.infobars.InfoBarLayout;
+
+/**
+ * An infobar that presents the user with several buttons.
+ *
+ * TODO(newt): merge this into InfoBar.java.
+ */
+public class ConfirmInfoBar extends InfoBar {
+ /** Text shown on the primary button, e.g. "OK". */
+ private final String mPrimaryButtonText;
+
+ /** Text shown on the secondary button, e.g. "Cancel".*/
+ private final String mSecondaryButtonText;
+
+ /** Text shown on the link, e.g. "Learn more". */
+ private final String mLinkText;
+
+ protected ConfirmInfoBar(int iconDrawableId, @ColorRes int iconTintId, Bitmap iconBitmap,
+ String message, String linkText, String primaryButtonText, String secondaryButtonText) {
+ super(iconDrawableId, iconTintId, message, iconBitmap);
+ mPrimaryButtonText = primaryButtonText;
+ mSecondaryButtonText = secondaryButtonText;
+ mLinkText = linkText;
+ }
+
+ @Override
+ public void createContent(InfoBarLayout layout) {
+ setButtons(layout, mPrimaryButtonText, mSecondaryButtonText);
+ if (mLinkText != null && !mLinkText.isEmpty()) layout.appendMessageLinkText(mLinkText);
+ }
+
+ /**
+ * If your custom infobar overrides this function, YOU'RE PROBABLY DOING SOMETHING WRONG.
+ *
+ * Adds buttons to the infobar. This should only be overridden in cases where an infobar
+ * requires adding something other than a button for its secondary View on the bottom row
+ * (almost never).
+ *
+ * @param primaryText Text to display on the primary button.
+ * @param secondaryText Text to display on the secondary button. May be null.
+ */
+ protected void setButtons(InfoBarLayout layout, String primaryText, String secondaryText) {
+ layout.setButtons(primaryText, secondaryText);
+ }
+
+ @Override
+ public void onButtonClicked(final boolean isPrimaryButton) {
+ int action = isPrimaryButton ? ActionType.OK : ActionType.CANCEL;
+ onButtonClicked(action);
+ }
+
+ /**
+ * Creates and begins the process for showing a ConfirmInfoBar.
+ * @param iconId ID corresponding to the icon that will be shown for the infobar.
+ * @param iconBitmap Bitmap to use if there is no equivalent Java resource for
+ * iconId.
+ * @param message Message to display to the user indicating what the infobar is for.
+ * @param linkText Link text to display in addition to the message.
+ * @param buttonOk String to display on the OK button.
+ * @param buttonCancel String to display on the Cancel button.
+ */
+ @CalledByNative
+ private static ConfirmInfoBar create(int iconId, Bitmap iconBitmap, String message,
+ String linkText, String buttonOk, String buttonCancel) {
+ ConfirmInfoBar infoBar = new ConfirmInfoBar(
+ iconId, 0, iconBitmap, message, linkText, buttonOk, buttonCancel);
+
+ return infoBar;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
deleted file mode 100644
index 44accce66d8..00000000000
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
+++ /dev/null
@@ -1,519 +0,0 @@
-// 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.RelativeLayout;
-
-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 RelativeLayout
- 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 TabImpl mTab;
- private WebContents mWebContents;
- private boolean mIsObscuredForAccessibility;
- 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;
- }
-
- protected TabImpl getTab() {
- return mTab;
- }
-
- public void setTab(TabImpl tab) {
- mTab = tab;
- boolean wasFocused = isFocused();
- boolean wasWindowFocused = hasWindowFocus();
- boolean wasAttached = isAttachedToWindow();
- boolean wasObscured = mIsObscuredForAccessibility;
- if (wasFocused) onFocusChanged(false, View.FOCUS_FORWARD, null);
- if (wasWindowFocused) onWindowFocusChanged(false);
- if (wasAttached) onDetachedFromWindow();
- if (wasObscured) setIsObscuredForAccessibility(false);
- mWebContents = mTab != null ? mTab.getWebContents() : null;
- if (wasFocused) onFocusChanged(true, View.FOCUS_FORWARD, null);
- if (wasWindowFocused) onWindowFocusChanged(true);
- if (wasAttached) onAttachedToWindow();
- if (wasObscured) setIsObscuredForAccessibility(true);
- }
-
- /**
- * Control whether WebContentsAccessibility will respond to accessibility requests.
- */
- public void setIsObscuredForAccessibility(boolean isObscured) {
- if (mIsObscuredForAccessibility == isObscured) return;
- mIsObscuredForAccessibility = isObscured;
- WebContentsAccessibility wcax = getWebContentsAccessibility();
- if (wcax == null) return;
- wcax.setObscuredByAnotherView(mIsObscuredForAccessibility);
- }
-
- @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) {
- EventForwarder forwarder = getEventForwarder();
- return forwarder != null ? forwarder.onKeyUp(keyCode, event) : false;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (!isFocused()) return super.dispatchKeyEvent(event);
- EventForwarder forwarder = getEventForwarder();
- return forwarder != null ? forwarder.dispatchKeyEvent(event) : false;
- }
-
- @Override
- public boolean onDragEvent(DragEvent event) {
- EventForwarder forwarder = getEventForwarder();
- return forwarder != null ? forwarder.onDragEvent(event, this) : false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent e) {
- boolean ret = super.onInterceptTouchEvent(e);
- mEventOffsetHandler.onInterceptTouchEvent(e);
- return ret;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- EventForwarder forwarder = getEventForwarder();
- boolean ret = forwarder != null ? forwarder.onTouchEvent(event) : false;
- 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) {
- EventForwarder forwarder = getEventForwarder();
- boolean consumed = forwarder != null ? forwarder.onHoverEvent(event) : false;
- WebContentsAccessibility wcax = getWebContentsAccessibility();
- if (wcax != null && !wcax.isTouchExplorationEnabled()) super.onHoverEvent(event);
- return consumed;
- }
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event) {
- EventForwarder forwarder = getEventForwarder();
- return forwarder != null ? forwarder.onGenericMotionEvent(event) : false;
- }
-
- 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) {
- EventForwarder forwarder = getEventForwarder();
- if (forwarder != null) forwarder.scrollBy(x, y);
- }
-
- @Override
- public void scrollTo(int x, int y) {
- EventForwarder forwarder = getEventForwarder();
- if (forwarder != null) forwarder.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 RelativeLayout 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
index b74f5266590..ac0235eaeb2 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java
@@ -86,6 +86,7 @@ public class ContentViewRenderView extends RelativeLayout {
int width, int height);
// |cacheBackBuffer| will delay destroying the EGLSurface until after the next swap.
void surfaceDestroyed(boolean cacheBackBuffer);
+ void surfaceRedrawNeededAsync(Runnable drawingFinished);
}
private final ArrayList<TrackedRunnable> mPendingRunnables = new ArrayList<>();
@@ -160,6 +161,11 @@ public class ContentViewRenderView extends RelativeLayout {
mNativeContentViewRenderView, cacheBackBuffer);
mCompositorHasSurface = false;
}
+
+ @Override
+ public void surfaceRedrawNeededAsync(Runnable drawingFinished) {
+ assert false; // NOTREACHED.
+ }
}
// Abstract differences between SurfaceView and TextureView behind this class.
@@ -220,6 +226,7 @@ public class ContentViewRenderView extends RelativeLayout {
private final TextureViewSurfaceTextureListener mSurfaceTextureListener;
private final ArrayList<ValueCallback<Boolean>> mModeCallbacks = new ArrayList<>();
+ private ArrayList<Runnable> mSurfaceRedrawNeededCallbacks;
public SurfaceData(@Mode int mode, FrameLayout parent, SurfaceEventListener listener,
int backgroundColor, Runnable evict) {
@@ -302,6 +309,7 @@ public class ContentViewRenderView extends RelativeLayout {
mListener.surfaceDestroyed(mCachedSurfaceNeedsEviction);
mNeedsOnSurfaceDestroyed = false;
}
+ runSurfaceRedrawNeededCallbacks();
if (mMode == MODE_SURFACE_VIEW) {
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
@@ -403,6 +411,15 @@ public class ContentViewRenderView extends RelativeLayout {
return false;
}
+ public void runSurfaceRedrawNeededCallbacks() {
+ ArrayList<Runnable> callbacks = mSurfaceRedrawNeededCallbacks;
+ mSurfaceRedrawNeededCallbacks = null;
+ if (callbacks == null) return;
+ for (Runnable r : callbacks) {
+ r.run();
+ }
+ }
+
private void destroyPreviousData() {
if (mPrevSurfaceDataNeedsDestroy != null) {
mPrevSurfaceDataNeedsDestroy.destroy();
@@ -445,6 +462,22 @@ public class ContentViewRenderView extends RelativeLayout {
assert mNeedsOnSurfaceDestroyed;
mListener.surfaceDestroyed(cacheBackBuffer);
mNeedsOnSurfaceDestroyed = false;
+ runSurfaceRedrawNeededCallbacks();
+ }
+
+ @Override
+ public void surfaceRedrawNeededAsync(Runnable drawingFinished) {
+ if (mMarkedForDestroy) {
+ drawingFinished.run();
+ return;
+ }
+ assert mNativeContentViewRenderView != 0;
+ assert this == ContentViewRenderView.this.mCurrent;
+ if (mSurfaceRedrawNeededCallbacks == null) {
+ mSurfaceRedrawNeededCallbacks = new ArrayList<>();
+ }
+ mSurfaceRedrawNeededCallbacks.add(drawingFinished);
+ ContentViewRenderViewJni.get().setNeedsRedraw(mNativeContentViewRenderView);
}
private void runCallbacks() {
@@ -470,7 +503,7 @@ public class ContentViewRenderView extends RelativeLayout {
}
// Adapter for SurfaceHoolder.Callback.
- private static class SurfaceHolderCallback implements SurfaceHolder.Callback {
+ private static class SurfaceHolderCallback implements SurfaceHolder.Callback2 {
private final SurfaceEventListener mListener;
public SurfaceHolderCallback(SurfaceEventListener listener) {
@@ -491,6 +524,16 @@ public class ContentViewRenderView extends RelativeLayout {
public void surfaceDestroyed(SurfaceHolder holder) {
mListener.surfaceDestroyed(false /* cacheBackBuffer */);
}
+
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ // Intentionally not implemented.
+ }
+
+ @Override
+ public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) {
+ mListener.surfaceRedrawNeededAsync(drawingFinished);
+ }
}
// Adapter for TextureView.SurfaceTextureListener.
@@ -697,7 +740,9 @@ public class ContentViewRenderView extends RelativeLayout {
mWebContents = webContents;
if (webContents != null) {
- updateWebContentsSize();
+ if (getWidth() != 0 && getHeight() != 0) {
+ updateWebContentsSize();
+ }
ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged(
mNativeContentViewRenderView, webContents, mPhysicalWidth, mPhysicalHeight);
}
@@ -719,6 +764,13 @@ public class ContentViewRenderView extends RelativeLayout {
return mCurrent.didSwapFrame();
}
+ @CalledByNative
+ private void didSwapBuffers(boolean sizeMatches) {
+ assert mCurrent != null;
+ if (!sizeMatches) return;
+ mCurrent.runSurfaceRedrawNeededCallbacks();
+ }
+
private void evictCachedSurface() {
if (mNativeContentViewRenderView == 0) return;
ContentViewRenderViewJni.get().evictCachedSurface(mNativeContentViewRenderView);
@@ -752,6 +804,7 @@ public class ContentViewRenderView extends RelativeLayout {
void surfaceDestroyed(long nativeContentViewRenderView, boolean cacheBackBuffer);
void surfaceChanged(long nativeContentViewRenderView, boolean canBeUsedWithSurfaceControl,
int format, int width, int height, Surface surface);
+ void setNeedsRedraw(long nativeContentViewRenderView);
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
index 1f147c0467c..5318bcc20d0 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java
@@ -204,6 +204,7 @@ public final class CrashReporterControllerImpl extends ICrashReporterController.
private String[] processNewMinidumpsOnBackgroundThread() {
Map<String, Map<String, String>> crashesInfoMap =
getCrashFileManager().importMinidumpsCrashKeys();
+ if (crashesInfoMap == null) return new String[0];
ArrayList<String> localIds = new ArrayList<>(crashesInfoMap.size());
for (Map.Entry<String, Map<String, String>> entry : crashesInfoMap.entrySet()) {
JSONObject crashKeysJson = new JSONObject(entry.getValue());
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
index 694e8839498..b1046065bc3 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
@@ -24,7 +24,6 @@ import org.chromium.components.browser_ui.notifications.NotificationManagerProxy
import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
import org.chromium.components.browser_ui.notifications.NotificationMetadata;
import org.chromium.components.browser_ui.notifications.PendingIntentProvider;
-import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
import org.chromium.components.browser_ui.util.DownloadUtils;
import org.chromium.weblayer_private.interfaces.APICallException;
import org.chromium.weblayer_private.interfaces.DownloadError;
@@ -342,18 +341,14 @@ public final class DownloadImpl extends IDownload.Stub {
PendingIntentProvider deletePendingIntent =
PendingIntentProvider.getBroadcast(context, mNotificationId, deleteIntent, 0);
- ChannelsInitializer channelsInitializer = new ChannelsInitializer(notificationManager,
- WebLayerNotificationChannels.getInstance(), context.getResources());
-
@DownloadState
int state = getState();
String channelId = state == DownloadState.COMPLETE
? WebLayerNotificationChannels.ChannelId.COMPLETED_DOWNLOADS
: WebLayerNotificationChannels.ChannelId.ACTIVE_DOWNLOADS;
- WebLayerNotificationBuilder builder =
- new WebLayerNotificationBuilder(context, channelId, channelsInitializer,
- new NotificationMetadata(0, NOTIFICATION_TAG, mNotificationId));
+ WebLayerNotificationBuilder builder = WebLayerNotificationBuilder.create(
+ channelId, new NotificationMetadata(0, NOTIFICATION_TAG, mNotificationId));
builder.setOngoing(true)
.setDeleteIntent(deletePendingIntent)
.setPriorityBeforeO(NotificationCompat.PRIORITY_DEFAULT);
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
index 6cd383bebbf..253c35212d0 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
@@ -8,6 +8,8 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import androidx.annotation.Nullable;
+
import org.chromium.base.ContextUtils;
import org.chromium.base.PackageManagerUtils;
import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -16,14 +18,18 @@ import org.chromium.components.external_intents.ExternalNavigationDelegate.Start
import org.chromium.components.external_intents.ExternalNavigationHandler;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
import org.chromium.components.external_intents.ExternalNavigationParams;
+import org.chromium.components.webapk.lib.client.ChromeWebApkHostSignature;
+import org.chromium.components.webapk.lib.client.WebApkValidator;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.Origin;
/**
* WebLayer's implementation of the {@link ExternalNavigationDelegate}.
*/
public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegate {
+ private static boolean sWebApkValidatorInitialized;
private final TabImpl mTab;
private boolean mTabDestroyed;
@@ -154,7 +160,8 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat
@Override
// This is relevant only if the intent ends up being handled by this app, which does not happen
// for WebLayer.
- public void maybeSetUserGesture(Intent intent) {}
+ public void maybeSetRequestMetadata(Intent intent, boolean hasUserGesture,
+ boolean isRendererInitiated, @Nullable Origin initiatorOrigin) {}
@Override
// This is relevant only if the intent ends up being handled by this app, which does not happen
@@ -205,8 +212,12 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat
@Override
public boolean isValidWebApk(String packageName) {
- // TODO(crbug.com/1063874): Determine whether to refine this.
- return false;
+ if (!sWebApkValidatorInitialized) {
+ WebApkValidator.init(ChromeWebApkHostSignature.EXPECTED_SIGNATURE,
+ ChromeWebApkHostSignature.PUBLIC_KEY);
+ sWebApkValidatorInitialized = true;
+ }
+ return WebApkValidator.isValidWebApk(ContextUtils.getApplicationContext(), packageName);
}
@Override
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java
new file mode 100644
index 00000000000..69e4754d98b
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java
@@ -0,0 +1,331 @@
+// Copyright 2020 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.Bitmap;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
+import org.chromium.components.infobars.InfoBarInteractionHandler;
+import org.chromium.components.infobars.InfoBarLayout;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * The base class for all InfoBar classes.
+ * Note that infobars expire by default when a new navigation occurs.
+ * Make sure to use setExpireOnNavigation(false) if you want an infobar to be sticky.
+ */
+@JNINamespace("weblayer")
+public abstract class InfoBar implements InfoBarInteractionHandler, InfoBarUiItem {
+ private static final String TAG = "InfoBar";
+
+ /**
+ * Interface for InfoBar to interact with its container.
+ */
+ public interface Container {
+ /**
+ * @return True if the infobar is in front.
+ */
+ boolean isFrontInfoBar(InfoBar infoBar);
+
+ /**
+ * Remove the infobar from its container.
+ * @param infoBar InfoBar to remove from the View hierarchy.
+ */
+ void removeInfoBar(InfoBar infoBar);
+
+ /**
+ * Notifies that an infobar's View ({@link InfoBar#getView}) has changed.
+ */
+ void notifyInfoBarViewChanged();
+
+ /**
+ * @return True if the container's destroy() method has been called.
+ */
+ boolean isDestroyed();
+ }
+
+ private final int mIconDrawableId;
+ private final Bitmap mIconBitmap;
+ private final @ColorRes int mIconTintId;
+ private final CharSequence mMessage;
+
+ private @Nullable Container mContainer;
+ private @Nullable View mView;
+ private @Nullable Context mContext;
+
+ private boolean mIsDismissed;
+ private boolean mControlsEnabled = true;
+
+ private @Nullable PropertyModel mModel;
+
+ // This points to the InfoBarAndroid class not any of its subclasses.
+ private long mNativeInfoBarPtr;
+
+ /**
+ * Constructor for regular infobars.
+ * @param iconDrawableId ID of the resource to use for the Icon. If 0, no icon will be shown.
+ * @param iconTintId The {@link ColorRes} used as tint for the {@code iconDrawableId}.
+ * @param message The message to show in the infobar.
+ * @param iconBitmap Icon to draw, in bitmap form. Used mainly for generated icons.
+ */
+ public InfoBar(
+ int iconDrawableId, @ColorRes int iconTintId, CharSequence message, Bitmap iconBitmap) {
+ mIconDrawableId = iconDrawableId;
+ mIconBitmap = iconBitmap;
+ mIconTintId = iconTintId;
+ mMessage = message;
+ }
+
+ /**
+ * Stores a pointer to the native-side counterpart of this InfoBar.
+ * @param nativeInfoBarPtr Pointer to the native InfoBarAndroid, not to its subclass.
+ */
+ @CalledByNative
+ private final void setNativeInfoBar(long nativeInfoBarPtr) {
+ mNativeInfoBarPtr = nativeInfoBarPtr;
+ }
+
+ @CalledByNative
+ protected void onNativeDestroyed() {
+ mNativeInfoBarPtr = 0;
+ }
+
+ /**
+ * Sets the Context used when creating the InfoBar.
+ */
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * @return The {@link Context} used to create the InfoBar. This will be null before the InfoBar
+ * is added to an {@link InfoBarContainer}, or after the InfoBar is closed.
+ */
+ @Nullable
+ protected Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Creates the View that represents the InfoBar.
+ * @return The View representing the InfoBar.
+ */
+ public final View createView() {
+ assert mContext != null;
+
+ if (usesCompactLayout()) {
+ InfoBarCompactLayout layout = new InfoBarCompactLayout(
+ mContext, this, mIconDrawableId, mIconTintId, mIconBitmap);
+ createCompactLayoutContent(layout);
+ mView = layout;
+ } else {
+ InfoBarLayout layout = new InfoBarLayout(
+ mContext, this, mIconDrawableId, mIconTintId, mIconBitmap, mMessage);
+ createContent(layout);
+ layout.onContentCreated();
+ mView = layout;
+ }
+
+ return mView;
+ }
+
+ /**
+ * @return The model for this infobar if one was created.
+ */
+ @Nullable
+ PropertyModel getModel() {
+ return mModel;
+ }
+
+ /**
+ * If this returns true, the infobar contents will be replaced with a one-line layout.
+ * When overriding this, also override {@link #getAccessibilityMessage}.
+ */
+ protected boolean usesCompactLayout() {
+ return false;
+ }
+
+ /**
+ * Prepares the InfoBar for display and adds InfoBar-specific controls to the layout.
+ * @param layout Layout containing all of the controls.
+ */
+ protected void createContent(InfoBarLayout layout) {}
+
+ /**
+ * Prepares and inserts views into an {@link InfoBarCompactLayout}.
+ * {@link #usesCompactLayout} must return 'true' for this function to be called.
+ * @param layout Layout to plug views into.
+ */
+ protected void createCompactLayoutContent(InfoBarCompactLayout layout) {}
+
+ /**
+ * Replaces the View currently shown in the infobar with the given View. Triggers the swap
+ * animation via the InfoBarContainer.
+ */
+ protected void replaceView(View newView) {
+ mView = newView;
+ mContainer.notifyInfoBarViewChanged();
+ }
+
+ /**
+ * Returns the View shown in this infobar. Only valid after createView() has been called.
+ */
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ /**
+ * Returns the accessibility message to announce when this infobar is first shown.
+ * Override this if the InfoBar doesn't have {@link R.id.infobar_message}. It is usually the
+ * case when it is in CompactLayout.
+ */
+ protected CharSequence getAccessibilityMessage(CharSequence defaultTitle) {
+ return defaultTitle == null ? "" : defaultTitle;
+ }
+
+ @Override
+ public CharSequence getAccessibilityText() {
+ if (mView == null) return "";
+
+ CharSequence title = null;
+ TextView messageView = (TextView) mView.findViewById(R.id.infobar_message);
+ if (messageView != null) {
+ title = messageView.getText();
+ }
+ title = getAccessibilityMessage(title);
+ if (title.length() > 0) {
+ title = title + " ";
+ }
+ // TODO(crbug/773717): Avoid string concatenation due to i18n.
+ return title + mContext.getString(R.string.weblayer_bottom_bar_screen_position);
+ }
+
+ @Override
+ public int getPriority() {
+ return InfoBarPriority.PAGE_TRIGGERED;
+ }
+
+ @Override
+ @InfoBarIdentifier
+ public int getInfoBarIdentifier() {
+ if (mNativeInfoBarPtr == 0) return InfoBarIdentifier.INVALID;
+ return InfoBarJni.get().getInfoBarIdentifier(mNativeInfoBarPtr, InfoBar.this);
+ }
+
+ /**
+ * @return whether the infobar actually needed closing.
+ */
+ @CalledByNative
+ private boolean closeInfoBar() {
+ if (!mIsDismissed) {
+ mIsDismissed = true;
+ if (!mContainer.isDestroyed()) {
+ // If the container was destroyed, it's already been emptied of all its infobars.
+ onStartedHiding();
+ mContainer.removeInfoBar(this);
+ }
+ mContainer = null;
+ mView = null;
+ mContext = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return If the infobar is the front infobar (i.e. visible and not hidden behind other
+ * infobars).
+ */
+ public boolean isFrontInfoBar() {
+ return mContainer.isFrontInfoBar(this);
+ }
+
+ /**
+ * Called just before the Java infobar has begun hiding. Give the chance to clean up any child
+ * UI that may remain open.
+ */
+ protected void onStartedHiding() {}
+
+ /**
+ * Returns pointer to native InfoBarAndroid instance.
+ * TODO(crbug/1056346): The function is used in subclasses typically to get Tab reference. When
+ * Tab is modularized, replace this function with the one that returns Tab reference.
+ */
+ protected long getNativeInfoBarPtr() {
+ return mNativeInfoBarPtr;
+ }
+
+ /**
+ * Sets the Container that displays the InfoBar.
+ */
+ public void setContainer(Container container) {
+ mContainer = container;
+ }
+
+ /**
+ * @return Whether or not this InfoBar is already dismissed (i.e. closed).
+ */
+ protected boolean isDismissed() {
+ return mIsDismissed;
+ }
+
+ @Override
+ public boolean areControlsEnabled() {
+ return mControlsEnabled;
+ }
+
+ @Override
+ public void setControlsEnabled(boolean state) {
+ mControlsEnabled = state;
+ }
+
+ @Override
+ public void onClick() {
+ setControlsEnabled(false);
+ }
+
+ @Override
+ public void onButtonClicked(boolean isPrimaryButton) {}
+
+ @Override
+ public void onLinkClicked() {
+ if (mNativeInfoBarPtr != 0) InfoBarJni.get().onLinkClicked(mNativeInfoBarPtr, InfoBar.this);
+ }
+
+ /**
+ * Performs some action related to the button being clicked.
+ * @param action The type of action defined in {@link ActionType} in this class.
+ */
+ protected void onButtonClicked(@ActionType int action) {
+ if (mNativeInfoBarPtr != 0) {
+ InfoBarJni.get().onButtonClicked(mNativeInfoBarPtr, InfoBar.this, action);
+ }
+ }
+
+ @Override
+ public void onCloseButtonClicked() {
+ if (mNativeInfoBarPtr != 0 && !mIsDismissed) {
+ InfoBarJni.get().onCloseButtonClicked(mNativeInfoBarPtr, InfoBar.this);
+ }
+ }
+
+ @NativeMethods
+ interface Natives {
+ int getInfoBarIdentifier(long nativeInfoBarAndroid, InfoBar caller);
+ void onLinkClicked(long nativeInfoBarAndroid, InfoBar caller);
+ void onButtonClicked(long nativeInfoBarAndroid, InfoBar caller, int action);
+ void onCloseButtonClicked(long nativeInfoBarAndroid, InfoBar caller);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java
new file mode 100644
index 00000000000..c2303eaea6b
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java
@@ -0,0 +1,238 @@
+// Copyright 2017 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.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.StringRes;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Callback;
+import org.chromium.components.infobars.InfoBarInteractionHandler;
+import org.chromium.components.infobars.InfoBarLayout;
+import org.chromium.components.infobars.InfoBarMessageView;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
+import org.chromium.ui.widget.ChromeImageButton;
+
+/**
+ * Lays out controls along a line, sandwiched between an (optional) icon and close button.
+ * This should only be used by the {@link InfoBar} class, and is created when the InfoBar subclass
+ * declares itself to be using a compact layout via {@link InfoBar#usesCompactLayout}.
+ */
+public class InfoBarCompactLayout extends LinearLayout implements View.OnClickListener {
+ private final InfoBarInteractionHandler mInfoBar;
+ private final int mCompactInfoBarSize;
+ private final int mIconWidth;
+ private final View mCloseButton;
+
+ /**
+ * Constructs a compat layout for the specified infobar.
+ * @param context The context used to render.
+ * @param infoBar {@link InfoBarInteractionHandler} that listens to events.
+ * @param iconResourceId Resource ID of the icon to use for the infobar.
+ * @param iconTintId The {@link ColorRes} used as tint for {@code iconResourceId}.
+ * @param iconBitmap Bitmap for the icon to use, if {@code iconResourceId} is not set.
+ */
+ // TODO(crbug/1056346): ctor is made public to allow access from InfoBar. Once
+ // InfoBar is modularized, restore access to package private.
+ public InfoBarCompactLayout(Context context, InfoBarInteractionHandler infoBar,
+ int iconResourceId, @ColorRes int iconTintId, Bitmap iconBitmap) {
+ super(context);
+ mInfoBar = infoBar;
+ mCompactInfoBarSize =
+ context.getResources().getDimensionPixelOffset(R.dimen.infobar_compact_size);
+ mIconWidth = context.getResources().getDimensionPixelOffset(R.dimen.infobar_big_icon_size);
+
+ setOrientation(LinearLayout.HORIZONTAL);
+ setGravity(Gravity.CENTER_VERTICAL);
+
+ prepareIcon(iconResourceId, iconTintId, iconBitmap);
+ mCloseButton = prepareCloseButton();
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view.getId() == R.id.infobar_close_button) {
+ mInfoBar.onCloseButtonClicked();
+ } else {
+ assert false;
+ }
+ }
+
+ /**
+ * Inserts a view before the close button.
+ * @param view View to insert.
+ * @param weight Weight to assign to it.
+ */
+ // TODO(crbug/1056346): addContent is made public to allow access from InfoBar. Once
+ // InfoBar is modularized, restore access to protected.
+ public void addContent(View view, float weight) {
+ LinearLayout.LayoutParams params;
+ if (weight <= 0.0f) {
+ params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, mCompactInfoBarSize);
+ } else {
+ params = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, weight);
+ }
+ view.setMinimumHeight(mCompactInfoBarSize);
+ params.gravity = Gravity.BOTTOM;
+ addView(view, indexOfChild(mCloseButton), params);
+ }
+
+ /**
+ * Adds an icon to the start of the infobar, if the infobar requires one.
+ * @param iconResourceId Resource ID of the icon to use.
+ * @param iconTintId The {@link ColorRes} used as tint for {@code iconResourceId}.
+ * @param iconBitmap Raw {@link Bitmap} to use instead of a resource.
+ */
+ private void prepareIcon(int iconResourceId, @ColorRes int iconTintId, Bitmap iconBitmap) {
+ ImageView iconView =
+ InfoBarLayout.createIconView(getContext(), iconResourceId, iconTintId, iconBitmap);
+ if (iconView != null) {
+ LinearLayout.LayoutParams iconParams =
+ new LinearLayout.LayoutParams(mIconWidth, mCompactInfoBarSize);
+ addView(iconView, iconParams);
+ }
+ }
+
+ /**
+ * Creates a close button that can be inserted into an infobar.
+ * NOTE: This was forked from //chrome's InfoBarLayout.java, as WebLayer supports only compact
+ * infobars and does not have a corresponding InfoBarLayout.java.
+ * @param context Context to grab resources from.
+ * @return {@link ImageButton} that represents a close button.
+ */
+ static ImageButton createCloseButton(Context context) {
+ final ColorStateList tint =
+ AppCompatResources.getColorStateList(context, R.color.default_icon_color);
+ TypedArray a =
+ context.obtainStyledAttributes(new int[] {android.R.attr.selectableItemBackground});
+ Drawable closeButtonBackground = a.getDrawable(0);
+ a.recycle();
+
+ ChromeImageButton closeButton = new ChromeImageButton(context);
+ closeButton.setId(R.id.infobar_close_button);
+ closeButton.setImageResource(R.drawable.btn_close);
+ ApiCompatibilityUtils.setImageTintList(closeButton, tint);
+ closeButton.setBackground(closeButtonBackground);
+ closeButton.setContentDescription(context.getString(R.string.weblayer_infobar_close));
+ closeButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+ return closeButton;
+ }
+
+ /** Adds a close button to the end of the infobar. */
+ private View prepareCloseButton() {
+ ImageButton closeButton = createCloseButton(getContext());
+ closeButton.setOnClickListener(this);
+ LinearLayout.LayoutParams closeParams =
+ new LinearLayout.LayoutParams(mCompactInfoBarSize, mCompactInfoBarSize);
+ addView(closeButton, closeParams);
+ return closeButton;
+ }
+
+ /**
+ * Helps building a standard message to display in a compact InfoBar. The message can feature
+ * a link to perform and action from this infobar.
+ */
+ public static class MessageBuilder {
+ private final InfoBarCompactLayout mLayout;
+ private CharSequence mMessage;
+ private CharSequence mLink;
+
+ /** @param layout The layout we are building a message view for. */
+ public MessageBuilder(InfoBarCompactLayout layout) {
+ mLayout = layout;
+ }
+
+ public MessageBuilder withText(CharSequence message) {
+ assert mMessage == null;
+ mMessage = message;
+
+ return this;
+ }
+
+ public MessageBuilder withText(@StringRes int messageResId) {
+ assert mMessage == null;
+ mMessage = mLayout.getResources().getString(messageResId);
+
+ return this;
+ }
+
+ /** Appends a link after the main message, its displayed text being the specified string. */
+ public MessageBuilder withLink(CharSequence label, Callback<View> onTapCallback) {
+ assert mLink == null;
+
+ final Resources resources = mLayout.getResources();
+ SpannableString link = new SpannableString(label);
+ link.setSpan(new NoUnderlineClickableSpan(resources, onTapCallback), 0, label.length(),
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ mLink = link;
+
+ return this;
+ }
+
+ /**
+ * Appends a link after the main message, its displayed text being constructed from the
+ * given resource ID.
+ */
+ public MessageBuilder withLink(@StringRes int textResId, Callback<View> onTapCallback) {
+ final Resources resources = mLayout.getResources();
+ String label = resources.getString(textResId);
+ return withLink(label, onTapCallback);
+ }
+
+ /** Finalizes the message view as set up in the builder and inserts it into the layout. */
+ public void buildAndInsert() {
+ mLayout.addContent(build(), 1f);
+ }
+
+ /**
+ * Finalizes the message view as set up in the builder. The caller is responsible for adding
+ * it to the parent layout.
+ */
+ public View build() {
+ // TODO(dgn): Should be able to handle ReaderMode and Survey infobars but they have non
+ // standard interaction models (no button/link, whole bar is a button) or style (large
+ // rather than default text). Revisit after snowflake review.
+
+ assert mMessage != null;
+
+ final int messagePadding = mLayout.getResources().getDimensionPixelOffset(
+ R.dimen.infobar_compact_message_vertical_padding);
+
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(mMessage);
+ if (mLink != null) builder.append(" ").append(mLink);
+
+ TextView prompt = new InfoBarMessageView(mLayout.getContext());
+ ApiCompatibilityUtils.setTextAppearance(
+ prompt, R.style.TextAppearance_TextMedium_Primary);
+ prompt.setText(builder);
+ prompt.setGravity(Gravity.CENTER_VERTICAL);
+ prompt.setPadding(0, messagePadding, 0, messagePadding);
+
+ if (mLink != null) prompt.setMovementMethod(LinkMovementMethod.getInstance());
+
+ return prompt;
+ }
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java
new file mode 100644
index 00000000000..15037971602
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java
@@ -0,0 +1,486 @@
+// Copyright 2013 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.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.ObserverList;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
+import org.chromium.content_public.browser.NavigationHandle;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
+import org.chromium.ui.util.AccessibilityUtil;
+
+import java.util.ArrayList;
+
+/**
+ * A container for all the infobars of a specific tab.
+ * Note that infobars creation can be initiated from Java or from native code.
+ * When initiated from native code, special code is needed to keep the Java and native infobar in
+ * sync, see NativeInfoBar.
+ */
+@JNINamespace("weblayer")
+public class InfoBarContainer implements KeyboardVisibilityListener, InfoBar.Container {
+ private static final String TAG = "InfoBarContainer";
+
+ // Number of instances that have not been destroyed.
+ private static int sInstanceCount;
+
+ // InfoBarContainer's handling of accessibility is a global toggle, and thus a static observer
+ // suffices. However, observing accessibility events has the wrinkle that all accessibility
+ // observers are removed when there are no more Browsers and are not re-added if a new Browser
+ // is subsequently created. To handle this wrinkle, |sAccessibilityObserver| is added as an
+ // observer whenever the number of non-destroyed InfoBarContainers becomes non-zero and removed
+ // whenever that number flips to zero.
+ private static final AccessibilityUtil.Observer sAccessibilityObserver;
+ static {
+ sAccessibilityObserver = (enabled) -> setIsAllowedToAutoHide(!enabled);
+ }
+
+ /**
+ * A listener for the InfoBar animations.
+ */
+ public interface InfoBarAnimationListener {
+ public static final int ANIMATION_TYPE_SHOW = 0;
+ public static final int ANIMATION_TYPE_SWAP = 1;
+ public static final int ANIMATION_TYPE_HIDE = 2;
+
+ /**
+ * Notifies the subscriber when an animation is completed.
+ */
+ void notifyAnimationFinished(int animationType);
+
+ /**
+ * Notifies the subscriber when all animations are finished.
+ * @param frontInfoBar The frontmost infobar or {@code null} if none are showing.
+ */
+ void notifyAllAnimationsFinished(InfoBarUiItem frontInfoBar);
+ }
+
+ /**
+ * An observer that is notified of changes to a {@link InfoBarContainer} object.
+ */
+ public interface InfoBarContainerObserver {
+ /**
+ * Called when an {@link InfoBar} is about to be added (before the animation).
+ * @param container The notifying {@link InfoBarContainer}
+ * @param infoBar An {@link InfoBar} being added
+ * @param isFirst Whether the infobar container was empty
+ */
+ void onAddInfoBar(InfoBarContainer container, InfoBar infoBar, boolean isFirst);
+
+ /**
+ * Called when an {@link InfoBar} is about to be removed (before the animation).
+ * @param container The notifying {@link InfoBarContainer}
+ * @param infoBar An {@link InfoBar} being removed
+ * @param isLast Whether the infobar container is going to be empty
+ */
+ void onRemoveInfoBar(InfoBarContainer container, InfoBar infoBar, boolean isLast);
+
+ /**
+ * Called when the InfobarContainer is attached to the window.
+ * @param hasInfobars True if infobar container has infobars to show.
+ */
+ void onInfoBarContainerAttachedToWindow(boolean hasInfobars);
+
+ /**
+ * A notification that the shown ratio of the infobar container has changed.
+ * @param container The notifying {@link InfoBarContainer}
+ * @param shownRatio The shown ratio of the infobar container.
+ */
+ void onInfoBarContainerShownRatioChanged(InfoBarContainer container, float shownRatio);
+ }
+
+ /**
+ * Resets the visibility of the InfoBarContainer when the user navigates, following Chrome's
+ * behavior. In particular in Chrome some features hide the infobar container. This hiding is
+ * always on a per-URL basis that should be undone on navigation. While no feature in WebLayer
+ * yet does this, we put this * defensive behavior in place so that any such added features
+ * don't end up inadvertently hiding the infobar container "forever" in a given tab.
+ */
+ private final WebContentsObserver mWebContentsObserver = new WebContentsObserver() {
+ @Override
+ public void didFinishNavigation(NavigationHandle navigation) {
+ if (navigation.hasCommitted() && navigation.isInMainFrame()) {
+ setHidden(false);
+ }
+ }
+ };
+
+ public void onTabDidGainActive() {
+ initializeContainerView(mTab.getBrowser().getContext());
+ updateWebContents();
+ mInfoBarContainerView.addToParentView();
+ }
+
+ public void onTabDidLoseActive() {
+ mInfoBarContainerView.removeFromParentView();
+ destroyContainerView();
+ }
+
+ /** The list of all InfoBars in this container, regardless of whether they've been shown yet. */
+ private final ArrayList<InfoBar> mInfoBars = new ArrayList<>();
+
+ private final ObserverList<InfoBarContainerObserver> mObservers = new ObserverList<>();
+ private final ObserverList<InfoBarAnimationListener> mAnimationListeners = new ObserverList<>();
+
+ private final InfoBarContainerView.ContainerViewObserver mContainerViewObserver =
+ new InfoBarContainerView.ContainerViewObserver() {
+ @Override
+ public void notifyAnimationFinished(int animationType) {
+ for (InfoBarAnimationListener listener : mAnimationListeners) {
+ listener.notifyAnimationFinished(animationType);
+ }
+ }
+
+ @Override
+ public void notifyAllAnimationsFinished(InfoBarUiItem frontInfoBar) {
+ for (InfoBarAnimationListener listener : mAnimationListeners) {
+ listener.notifyAllAnimationsFinished(frontInfoBar);
+ }
+ }
+
+ @Override
+ public void onShownRatioChanged(float shownFraction) {
+ for (InfoBarContainer.InfoBarContainerObserver observer : mObservers) {
+ observer.onInfoBarContainerShownRatioChanged(
+ InfoBarContainer.this, shownFraction);
+ }
+ }
+ };
+
+ /** The tab that hosts this infobar container. */
+ private final TabImpl mTab;
+
+ /** Native InfoBarContainer pointer which will be set by InfoBarContainerJni.get().init(). */
+ private long mNativeInfoBarContainer;
+
+ /** True when this container has been emptied and its native counterpart has been destroyed. */
+ private boolean mDestroyed;
+
+ /** Whether or not this View should be hidden. */
+ private boolean mIsHidden;
+
+ /**
+ * The view for this {@link InfoBarContainer}. It will be null when the {@link Tab} is detached
+ * from a {@link ChromeActivity}.
+ */
+ private @Nullable InfoBarContainerView mInfoBarContainerView;
+
+ InfoBarContainer(TabImpl tab) {
+ if (++sInstanceCount == 1) {
+ WebLayerAccessibilityUtil.get().addObserver(sAccessibilityObserver);
+ }
+
+ mTab = tab;
+ mTab.getWebContents().addObserver(mWebContentsObserver);
+
+ // Chromium's InfoBarContainer may add an InfoBar immediately during this initialization
+ // call, so make sure everything in the InfoBarContainer is completely ready beforehand.
+ mNativeInfoBarContainer = InfoBarContainerJni.get().init(InfoBarContainer.this);
+ }
+
+ /**
+ * Adds an {@link InfoBarContainerObserver}.
+ * @param observer The {@link InfoBarContainerObserver} to add.
+ */
+ public void addObserver(InfoBarContainerObserver observer) {
+ mObservers.addObserver(observer);
+ }
+
+ /**
+ * Removes a {@link InfoBarContainerObserver}.
+ * @param observer The {@link InfoBarContainerObserver} to remove.
+ */
+ public void removeObserver(InfoBarContainerObserver observer) {
+ mObservers.removeObserver(observer);
+ }
+
+ /**
+ * Sets the parent {@link ViewGroup} that contains the {@link InfoBarContainer}.
+ */
+ public void setParentView(ViewGroup parent) {
+ assert mTab.getBrowser().getActiveTab() == mTab;
+ if (mInfoBarContainerView != null) mInfoBarContainerView.setParentView(parent);
+ }
+
+ @VisibleForTesting
+ public void addAnimationListener(InfoBarAnimationListener listener) {
+ mAnimationListeners.addObserver(listener);
+ }
+
+ /**
+ * Removes the passed in {@link InfoBarAnimationListener} from the {@link InfoBarContainer}.
+ */
+ public void removeAnimationListener(InfoBarAnimationListener listener) {
+ mAnimationListeners.removeObserver(listener);
+ }
+
+ /**
+ * Adds an InfoBar to the view hierarchy.
+ * @param infoBar InfoBar to add to the View hierarchy.
+ */
+ @CalledByNative
+ private void addInfoBar(InfoBar infoBar) {
+ assert !mDestroyed;
+ if (infoBar == null) {
+ return;
+ }
+ if (mInfoBars.contains(infoBar)) {
+ assert false : "Trying to add an info bar that has already been added.";
+ return;
+ }
+
+ infoBar.setContext(mInfoBarContainerView.getContext());
+ infoBar.setContainer(this);
+
+ // We notify observers immediately (before the animation starts).
+ for (InfoBarContainerObserver observer : mObservers) {
+ observer.onAddInfoBar(this, infoBar, mInfoBars.isEmpty());
+ }
+
+ assert mInfoBarContainerView != null : "The container view is null when adding an InfoBar";
+
+ // We add the infobar immediately to mInfoBars but we wait for the animation to end to
+ // notify it's been added, as tests rely on this notification but expects the infobar view
+ // to be available when they get the notification.
+ mInfoBars.add(infoBar);
+
+ mInfoBarContainerView.addInfoBar(infoBar);
+ }
+
+ @VisibleForTesting
+ public View getViewForTesting() {
+ return mInfoBarContainerView;
+ }
+
+ /**
+ * Adds an InfoBar to the view hierarchy.
+ * @param infoBar InfoBar to add to the View hierarchy.
+ */
+ @VisibleForTesting
+ public void addInfoBarForTesting(InfoBar infoBar) {
+ addInfoBar(infoBar);
+ }
+
+ @Override
+ public void notifyInfoBarViewChanged() {
+ assert !mDestroyed;
+ if (mInfoBarContainerView != null) mInfoBarContainerView.notifyInfoBarViewChanged();
+ }
+
+ /**
+ * Sets the visibility for the {@link InfoBarContainerView}.
+ * @param visibility One of {@link View#GONE}, {@link View#INVISIBLE}, or {@link View#VISIBLE}.
+ */
+ public void setVisibility(int visibility) {
+ if (mInfoBarContainerView != null) mInfoBarContainerView.setVisibility(visibility);
+ }
+
+ /**
+ * @return The visibility of the {@link InfoBarContainerView}.
+ */
+ public int getVisibility() {
+ return mInfoBarContainerView != null ? mInfoBarContainerView.getVisibility() : View.GONE;
+ }
+
+ @Override
+ public void removeInfoBar(InfoBar infoBar) {
+ assert !mDestroyed;
+
+ if (!mInfoBars.remove(infoBar)) {
+ assert false : "Trying to remove an InfoBar that is not in this container.";
+ return;
+ }
+
+ // Notify observers immediately, before any animations begin.
+ for (InfoBarContainerObserver observer : mObservers) {
+ observer.onRemoveInfoBar(this, infoBar, mInfoBars.isEmpty());
+ }
+
+ assert mInfoBarContainerView
+ != null : "The container view is null when removing an InfoBar.";
+ mInfoBarContainerView.removeInfoBar(infoBar);
+ }
+
+ @Override
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
+
+ public void destroy() {
+ mTab.getWebContents().removeObserver(mWebContentsObserver);
+
+ if (--sInstanceCount == 0) {
+ WebLayerAccessibilityUtil.get().removeObserver(sAccessibilityObserver);
+ }
+
+ if (mInfoBarContainerView != null) destroyContainerView();
+ if (mNativeInfoBarContainer != 0) {
+ InfoBarContainerJni.get().destroy(mNativeInfoBarContainer, InfoBarContainer.this);
+ mNativeInfoBarContainer = 0;
+ }
+ mDestroyed = true;
+ }
+
+ /**
+ * @return all of the InfoBars held in this container.
+ */
+ @VisibleForTesting
+ public ArrayList<InfoBar> getInfoBarsForTesting() {
+ return mInfoBars;
+ }
+
+ /**
+ * @return True if the container has any InfoBars.
+ */
+ @CalledByNative
+ public boolean hasInfoBars() {
+ return !mInfoBars.isEmpty();
+ }
+
+ /**
+ * @return InfoBarIdentifier of the InfoBar which is currently at the top of the infobar stack,
+ * or InfoBarIdentifier.INVALID if there are no infobars.
+ */
+ @CalledByNative
+ private @InfoBarIdentifier int getTopInfoBarIdentifier() {
+ if (!hasInfoBars()) return InfoBarIdentifier.INVALID;
+ return mInfoBars.get(0).getInfoBarIdentifier();
+ }
+
+ /**
+ * Hides or stops hiding this View.
+ *
+ * @param isHidden Whether this View is should be hidden.
+ */
+ public void setHidden(boolean isHidden) {
+ mIsHidden = isHidden;
+ if (mInfoBarContainerView == null) return;
+ mInfoBarContainerView.setHidden(isHidden);
+ }
+
+ /**
+ * Sets whether the InfoBarContainer is allowed to auto-hide when the user scrolls the page.
+ * Expected to be called when Touch Exploration is enabled.
+ * @param isAllowed Whether auto-hiding is allowed.
+ */
+ private static void setIsAllowedToAutoHide(boolean isAllowed) {
+ InfoBarContainerView.setIsAllowedToAutoHide(isAllowed);
+ }
+
+ // KeyboardVisibilityListener implementation.
+ @Override
+ public void keyboardVisibilityChanged(boolean isKeyboardShowing) {
+ assert mInfoBarContainerView != null;
+ boolean isShowing = (mInfoBarContainerView.getVisibility() == View.VISIBLE);
+ if (isKeyboardShowing) {
+ if (isShowing) {
+ mInfoBarContainerView.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (!isShowing && !mIsHidden) {
+ mInfoBarContainerView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ private void updateWebContents() {
+ // When the tab is detached, we don't update the InfoBarContainer web content so that it
+ // stays null until the tab is attached to some ChromeActivity.
+ if (mInfoBarContainerView == null) return;
+ WebContents webContents = mTab.getWebContents();
+
+ if (webContents != null && webContents != mInfoBarContainerView.getWebContents()) {
+ mInfoBarContainerView.setWebContents(webContents);
+ if (mNativeInfoBarContainer != 0) {
+ InfoBarContainerJni.get().setWebContents(
+ mNativeInfoBarContainer, InfoBarContainer.this, webContents);
+ }
+ }
+ }
+
+ private void initializeContainerView(Context chromeActivity) {
+ assert chromeActivity
+ != null
+ : "ChromeActivity should not be null when initializing InfoBarContainerView";
+ mInfoBarContainerView = new InfoBarContainerView(chromeActivity, mContainerViewObserver,
+ mTab, /*isTablet=*/!mTab.getBrowser().isWindowOnSmallDevice());
+
+ mInfoBarContainerView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ for (InfoBarContainer.InfoBarContainerObserver observer : mObservers) {
+ observer.onInfoBarContainerAttachedToWindow(!mInfoBars.isEmpty());
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {}
+ });
+
+ mInfoBarContainerView.setHidden(mIsHidden);
+ setParentView(mTab.getBrowser().getViewController().getInfoBarContainerParentView());
+
+ mTab.getBrowser().getWindowAndroid().getKeyboardDelegate().addKeyboardVisibilityListener(
+ this);
+ }
+
+ private void destroyContainerView() {
+ if (mInfoBarContainerView != null) {
+ mInfoBarContainerView.setWebContents(null);
+ if (mNativeInfoBarContainer != 0) {
+ InfoBarContainerJni.get().setWebContents(
+ mNativeInfoBarContainer, InfoBarContainer.this, null);
+ }
+ mInfoBarContainerView.destroy();
+ mInfoBarContainerView = null;
+ }
+
+ mTab.getBrowser().getWindowAndroid().getKeyboardDelegate().removeKeyboardVisibilityListener(
+ this);
+ }
+
+ @Override
+ public boolean isFrontInfoBar(InfoBar infoBar) {
+ if (mInfoBars.isEmpty()) return false;
+ return mInfoBars.get(0) == infoBar;
+ }
+
+ /**
+ * Returns true if any animations are pending or in progress.
+ */
+ @VisibleForTesting
+ public boolean isAnimating() {
+ assert mInfoBarContainerView != null;
+ return mInfoBarContainerView.isAnimating();
+ }
+
+ /**
+ * @return The {@link InfoBarContainerView} this class holds.
+ */
+ @VisibleForTesting
+ public InfoBarContainerView getContainerViewForTesting() {
+ return mInfoBarContainerView;
+ }
+
+ @NativeMethods
+ interface Natives {
+ long init(InfoBarContainer caller);
+ void setWebContents(long nativeInfoBarContainerAndroid, InfoBarContainer caller,
+ WebContents webContents);
+ void destroy(long nativeInfoBarContainerAndroid, InfoBarContainer caller);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java
new file mode 100644
index 00000000000..4f91d6f437d
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java
@@ -0,0 +1,852 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import org.chromium.ui.widget.OptimizedFrameLayout;
+import org.chromium.weblayer_private.InfoBarContainer.InfoBarAnimationListener;
+
+import java.util.ArrayList;
+
+/**
+ * Layout that displays infobars in a stack. Handles all the animations when adding or removing
+ * infobars and when swapping infobar contents.
+ *
+ * The first infobar to be added is visible at the front of the stack. Later infobars peek up just
+ * enough behind the front infobar to signal their existence; their contents aren't visible at all.
+ * The stack has a max depth of three infobars. If additional infobars are added beyond this, they
+ * won't be visible at all until infobars in front of them are dismissed.
+ *
+ * Animation details:
+ * - Newly added infobars slide up from the bottom and then their contents fade in.
+ * - Disappearing infobars slide down and away. The remaining infobars, if any, resize to the
+ * new front infobar's size, then the content of the new front infobar fades in.
+ * - When swapping the front infobar's content, the old content fades out, the infobar resizes to
+ * the new content's size, then the new content fades in.
+ * - Only a single animation happens at a time. If several infobars are added and/or removed in
+ * quick succession, the animations will be queued and run sequentially.
+ *
+ * Note: this class depends only on Android view code; it intentionally does not depend on any other
+ * infobar code. This is an explicit design decision and should remain this way.
+ *
+ * TODO(newt): what happens when detached from window? Do animations run? Do animations jump to end
+ * values? Should they jump to end values? Does requestLayout() get called when detached
+ * from window? Probably not; it probably just gets called later when reattached.
+ *
+ * TODO(newt): use hardware acceleration? See
+ * http://blog.danlew.net/2015/10/20/using-hardware-layers-to-improve-animation-performance/
+ * and http://developer.android.com/guide/topics/graphics/hardware-accel.html#layers
+ *
+ * TODO(newt): handle tall infobars on small devices. Use a ScrollView inside the InfoBarWrapper?
+ * Make sure InfoBarContainerLayout doesn't extend into tabstrip on tablet.
+ *
+ * TODO(newt): Disable key events during animations, perhaps by overriding dispatchKeyEvent().
+ * Or can we just call setEnabled() false on the infobar wrapper? Will this cause the buttons
+ * visual state to change (i.e. to turn gray)?
+ *
+ * TODO(newt): finalize animation timings and interpolators.
+ */
+public class InfoBarContainerLayout extends OptimizedFrameLayout {
+ /**
+ * Creates an empty InfoBarContainerLayout.
+ */
+ InfoBarContainerLayout(Context context, Runnable makeContainerVisibleRunnable,
+ InfoBarAnimationListener animationListener) {
+ super(context, null);
+ Resources res = context.getResources();
+ mBackInfobarHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height);
+ mFloatingBehavior = new FloatingBehavior(this);
+ mAnimationListener = animationListener;
+ mMakeContainerVisibleRunnable = makeContainerVisibleRunnable;
+ }
+
+ /**
+ * Adds an infobar to the container. The infobar appearing animation will happen after the
+ * current animation, if any, finishes.
+ */
+ void addInfoBar(InfoBarUiItem item) {
+ mItems.add(findInsertIndex(item), item);
+ processPendingAnimations();
+ }
+
+ /**
+ * Finds the appropriate index in the infobar stack for inserting this item.
+ * @param item The infobar to be inserted.
+ */
+ private int findInsertIndex(InfoBarUiItem item) {
+ for (int i = 0; i < mItems.size(); ++i) {
+ if (item.getPriority() < mItems.get(i).getPriority()) {
+ return i;
+ }
+ }
+
+ return mItems.size();
+ }
+
+ /**
+ * Removes an infobar from the container. The infobar will be animated off the screen if it's
+ * currently visible.
+ */
+ void removeInfoBar(InfoBarUiItem item) {
+ mItems.remove(item);
+ processPendingAnimations();
+ }
+
+ /**
+ * Notifies that an infobar's View ({@link InfoBarUiItem#getView}) has changed. If the infobar
+ * is visible in the front of the stack, the infobar will fade out the old contents, resize,
+ * then fade in the new contents.
+ */
+ void notifyInfoBarViewChanged() {
+ processPendingAnimations();
+ }
+
+ /**
+ * Returns true if any animations are pending or in progress.
+ */
+ boolean isAnimating() {
+ return mAnimation != null;
+ }
+
+ /////////////////////////////////////////
+ // Implementation details
+ /////////////////////////////////////////
+
+ /** The maximum number of infobars visible at any time. */
+ private static final int MAX_STACK_DEPTH = 3;
+
+ // Animation durations.
+ private static final int DURATION_SLIDE_UP_MS = 250;
+ private static final int DURATION_SLIDE_DOWN_MS = 250;
+ private static final int DURATION_FADE_MS = 100;
+ private static final int DURATION_FADE_OUT_MS = 200;
+
+ /**
+ * Base class for animations inside the InfoBarContainerLayout.
+ *
+ * Provides a standardized way to prepare for, run, and clean up after animations. Each subclass
+ * should implement prepareAnimation(), createAnimator(), and onAnimationEnd() as needed.
+ */
+ private abstract class InfoBarAnimation {
+ private Animator mAnimator;
+
+ final boolean isStarted() {
+ return mAnimator != null;
+ }
+
+ final void start() {
+ Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ InfoBarAnimation.this.onAnimationEnd();
+ mAnimation = null;
+ mAnimationListener.notifyAnimationFinished(getAnimationType());
+ processPendingAnimations();
+ }
+ };
+
+ mAnimator = createAnimator();
+ mAnimator.addListener(listener);
+ mAnimator.start();
+ }
+
+ /**
+ * Returns an animator that animates an InfoBarWrapper's y-translation from its current
+ * value to endValue and updates the side shadow positions on each frame.
+ */
+ ValueAnimator createTranslationYAnimator(final InfoBarWrapper wrapper, float endValue) {
+ ValueAnimator animator = ValueAnimator.ofFloat(wrapper.getTranslationY(), endValue);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ wrapper.setTranslationY((float) animation.getAnimatedValue());
+ mFloatingBehavior.updateShadowPosition();
+ }
+ });
+ return animator;
+ }
+
+ /**
+ * Called before the animation begins. This is the time to add views to the hierarchy and
+ * adjust layout parameters.
+ */
+ void prepareAnimation() {}
+
+ /**
+ * Called to create an Animator which will control the animation. Called after
+ * prepareAnimation() and after a subsequent layout has happened.
+ */
+ abstract Animator createAnimator();
+
+ /**
+ * Called after the animation completes. This is the time to do post-animation cleanup, such
+ * as removing views from the hierarchy.
+ */
+ void onAnimationEnd() {}
+
+ /**
+ * Returns the InfoBarAnimationListener.ANIMATION_TYPE_* constant that corresponds to this
+ * type of animation (showing, swapping, etc).
+ */
+ abstract int getAnimationType();
+ }
+
+ /**
+ * The animation to show the first infobar. The infobar slides up from the bottom; then its
+ * content fades in.
+ */
+ private class FirstInfoBarAppearingAnimation extends InfoBarAnimation {
+ private InfoBarUiItem mFrontItem;
+ private InfoBarWrapper mFrontWrapper;
+ private View mFrontContents;
+
+ FirstInfoBarAppearingAnimation(InfoBarUiItem frontItem) {
+ mFrontItem = frontItem;
+ }
+
+ @Override
+ void prepareAnimation() {
+ mFrontContents = mFrontItem.getView();
+ mFrontWrapper = new InfoBarWrapper(getContext(), mFrontItem);
+ mFrontWrapper.addView(mFrontContents);
+ addWrapper(mFrontWrapper);
+ }
+
+ @Override
+ Animator createAnimator() {
+ mFrontWrapper.setTranslationY(mFrontWrapper.getHeight());
+ mFrontContents.setAlpha(0f);
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playSequentially(
+ createTranslationYAnimator(mFrontWrapper, 0f).setDuration(DURATION_SLIDE_UP_MS),
+ ObjectAnimator.ofFloat(mFrontContents, View.ALPHA, 1f)
+ .setDuration(DURATION_FADE_MS));
+ return animator;
+ }
+
+ @Override
+ void onAnimationEnd() {
+ announceForAccessibility(mFrontItem.getAccessibilityText());
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_SHOW;
+ }
+ }
+
+ /**
+ * The animation to show the a new front-most infobar in front of existing visible infobars. The
+ * infobar slides up from the bottom; then its content fades in. The previously visible infobars
+ * will be resized simulatenously to the new desired size.
+ */
+ private class FrontInfoBarAppearingAnimation extends InfoBarAnimation {
+ private InfoBarUiItem mFrontItem;
+ private InfoBarWrapper mFrontWrapper;
+ private InfoBarWrapper mOldFrontWrapper;
+ private View mFrontContents;
+
+ FrontInfoBarAppearingAnimation(InfoBarUiItem frontItem) {
+ mFrontItem = frontItem;
+ }
+
+ @Override
+ void prepareAnimation() {
+ mOldFrontWrapper = mInfoBarWrappers.get(0);
+
+ mFrontContents = mFrontItem.getView();
+ mFrontWrapper = new InfoBarWrapper(getContext(), mFrontItem);
+ mFrontWrapper.addView(mFrontContents);
+ addWrapperToFront(mFrontWrapper);
+ }
+
+ @Override
+ Animator createAnimator() {
+ // After adding the new wrapper, the new front item's view, and the old front item's
+ // view are both in their wrappers, and the height of the stack as determined by
+ // FrameLayout will take both into account. This means the height of the container will
+ // be larger than it needs to be, if the previous old front item is larger than the sum
+ // of the new front item and mBackInfobarHeight.
+ //
+ // First work out how much the container will grow or shrink by.
+ int heightDelta =
+ mFrontWrapper.getHeight() + mBackInfobarHeight - mOldFrontWrapper.getHeight();
+
+ // Now work out where to animate the new front item to / from.
+ int newFrontStart = mFrontWrapper.getHeight();
+ int newFrontEnd = 0;
+ if (heightDelta < 0) {
+ // If the container is shrinking, this won't be reflected in the layout just yet.
+ // The layout will have extra space in it for the previous front infobar, which the
+ // animation of the new front infobar has to take into account.
+ newFrontStart -= heightDelta;
+ newFrontEnd -= heightDelta;
+ }
+ mFrontWrapper.setTranslationY(newFrontStart);
+ mFrontContents.setAlpha(0f);
+
+ // Since we are adding the infobar to the top of the stack, make the container fully
+ // visible since it could be at hidden or partially hidden state.
+ mMakeContainerVisibleRunnable.run();
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.play(createTranslationYAnimator(mFrontWrapper, newFrontEnd)
+ .setDuration(DURATION_SLIDE_UP_MS));
+
+ // If the container is shrinking, the back infobars need to animate down (from 0 to the
+ // positive delta). Otherwise they have to animate up (from the negative delta to 0).
+ int backStart = Math.max(0, heightDelta);
+ int backEnd = Math.max(-heightDelta, 0);
+ for (int i = 1; i < mInfoBarWrappers.size(); i++) {
+ mInfoBarWrappers.get(i).setTranslationY(backStart);
+ animator.play(createTranslationYAnimator(mInfoBarWrappers.get(i), backEnd)
+ .setDuration(DURATION_SLIDE_UP_MS));
+ }
+
+ animator.play(ObjectAnimator.ofFloat(mFrontContents, View.ALPHA, 1f)
+ .setDuration(DURATION_FADE_MS))
+ .after(DURATION_SLIDE_UP_MS);
+
+ return animator;
+ }
+
+ @Override
+ void onAnimationEnd() {
+ // Remove the old front wrappers view so it won't affect the height of the container any
+ // more.
+ mOldFrontWrapper.removeAllViews();
+
+ // Now set any Y offsets to 0 as there is no need to account for the old front wrapper
+ // making the container higher than it should be.
+ for (int i = 0; i < mInfoBarWrappers.size(); i++) {
+ mInfoBarWrappers.get(i).setTranslationY(0);
+ }
+ updateLayoutParams();
+ announceForAccessibility(mFrontItem.getAccessibilityText());
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_SHOW;
+ }
+ }
+
+ /**
+ * The animation to show a back infobar. The infobar slides up behind the existing infobars, so
+ * its top edge peeks out just a bit.
+ */
+ private class BackInfoBarAppearingAnimation extends InfoBarAnimation {
+ private InfoBarWrapper mAppearingWrapper;
+
+ BackInfoBarAppearingAnimation(InfoBarUiItem appearingItem) {
+ mAppearingWrapper = new InfoBarWrapper(getContext(), appearingItem);
+ }
+
+ @Override
+ void prepareAnimation() {
+ addWrapper(mAppearingWrapper);
+ }
+
+ @Override
+ Animator createAnimator() {
+ mAppearingWrapper.setTranslationY(mAppearingWrapper.getHeight());
+ return createTranslationYAnimator(mAppearingWrapper, 0f)
+ .setDuration(DURATION_SLIDE_UP_MS);
+ }
+
+ @Override
+ public void onAnimationEnd() {
+ mAppearingWrapper.removeView(mAppearingWrapper.getItem().getView());
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_SHOW;
+ }
+ }
+
+ /**
+ * The animation to hide the front infobar and reveal the second-to-front infobar. The front
+ * infobar slides down and off the screen. The back infobar(s) will adjust to the size of the
+ * new front infobar, and then the new front infobar's contents will fade in.
+ */
+ private class FrontInfoBarDisappearingAndRevealingAnimation extends InfoBarAnimation {
+ private InfoBarWrapper mOldFrontWrapper;
+ private InfoBarWrapper mNewFrontWrapper;
+ private View mNewFrontContents;
+
+ @Override
+ void prepareAnimation() {
+ mOldFrontWrapper = mInfoBarWrappers.get(0);
+ mNewFrontWrapper = mInfoBarWrappers.get(1);
+ mNewFrontContents = mNewFrontWrapper.getItem().getView();
+ mNewFrontWrapper.addView(mNewFrontContents);
+ }
+
+ @Override
+ Animator createAnimator() {
+ // The amount by which mNewFrontWrapper will grow (negative value indicates shrinking).
+ int deltaHeight = (mNewFrontWrapper.getHeight() - mBackInfobarHeight)
+ - mOldFrontWrapper.getHeight();
+ int startTranslationY = Math.max(deltaHeight, 0);
+ int endTranslationY = Math.max(-deltaHeight, 0);
+
+ // Slide the front infobar down and away.
+ AnimatorSet animator = new AnimatorSet();
+ mOldFrontWrapper.setTranslationY(startTranslationY);
+ animator.play(createTranslationYAnimator(
+ mOldFrontWrapper, startTranslationY + mOldFrontWrapper.getHeight())
+ .setDuration(DURATION_SLIDE_UP_MS));
+
+ // Slide the other infobars to their new positions.
+ // Note: animator.play() causes these animations to run simultaneously.
+ for (int i = 1; i < mInfoBarWrappers.size(); i++) {
+ mInfoBarWrappers.get(i).setTranslationY(startTranslationY);
+ animator.play(createTranslationYAnimator(mInfoBarWrappers.get(i), endTranslationY)
+ .setDuration(DURATION_SLIDE_UP_MS));
+ }
+
+ mNewFrontContents.setAlpha(0f);
+ animator.play(ObjectAnimator.ofFloat(mNewFrontContents, View.ALPHA, 1f)
+ .setDuration(DURATION_FADE_MS))
+ .after(DURATION_SLIDE_UP_MS);
+
+ return animator;
+ }
+
+ @Override
+ void onAnimationEnd() {
+ mOldFrontWrapper.removeAllViews();
+ removeWrapper(mOldFrontWrapper);
+ for (int i = 0; i < mInfoBarWrappers.size(); i++) {
+ mInfoBarWrappers.get(i).setTranslationY(0);
+ }
+ announceForAccessibility(mNewFrontWrapper.getItem().getAccessibilityText());
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_HIDE;
+ }
+ }
+
+ /**
+ * The animation to hide the backmost infobar, or the front infobar if there's only one infobar.
+ * The infobar simply slides down out of the container.
+ */
+ private class InfoBarDisappearingAnimation extends InfoBarAnimation {
+ private InfoBarWrapper mDisappearingWrapper;
+
+ @Override
+ void prepareAnimation() {
+ mDisappearingWrapper = mInfoBarWrappers.get(mInfoBarWrappers.size() - 1);
+ }
+
+ @Override
+ Animator createAnimator() {
+ return createTranslationYAnimator(
+ mDisappearingWrapper, mDisappearingWrapper.getHeight())
+ .setDuration(DURATION_SLIDE_DOWN_MS);
+ }
+
+ @Override
+ void onAnimationEnd() {
+ mDisappearingWrapper.removeAllViews();
+ removeWrapper(mDisappearingWrapper);
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_HIDE;
+ }
+ }
+
+ /**
+ * The animation to swap the contents of the front infobar. The current contents fade out,
+ * then the infobar resizes to fit the new contents, then the new contents fade in.
+ */
+ private class FrontInfoBarSwapContentsAnimation extends InfoBarAnimation {
+ private InfoBarWrapper mFrontWrapper;
+ private View mOldContents;
+ private View mNewContents;
+
+ @Override
+ void prepareAnimation() {
+ mFrontWrapper = mInfoBarWrappers.get(0);
+ mOldContents = mFrontWrapper.getChildAt(0);
+ mNewContents = mFrontWrapper.getItem().getView();
+ mFrontWrapper.addView(mNewContents);
+ }
+
+ @Override
+ Animator createAnimator() {
+ int deltaHeight = mNewContents.getHeight() - mOldContents.getHeight();
+ InfoBarContainerLayout.this.setTranslationY(Math.max(0, deltaHeight));
+ mNewContents.setAlpha(0f);
+
+ AnimatorSet animator = new AnimatorSet();
+ animator.playSequentially(ObjectAnimator.ofFloat(mOldContents, View.ALPHA, 0f)
+ .setDuration(DURATION_FADE_OUT_MS),
+ ObjectAnimator
+ .ofFloat(InfoBarContainerLayout.this, View.TRANSLATION_Y,
+ Math.max(0, -deltaHeight))
+ .setDuration(DURATION_SLIDE_UP_MS),
+ ObjectAnimator.ofFloat(mNewContents, View.ALPHA, 1f)
+ .setDuration(DURATION_FADE_OUT_MS));
+ return animator;
+ }
+
+ @Override
+ void onAnimationEnd() {
+ mFrontWrapper.removeViewAt(0);
+ InfoBarContainerLayout.this.setTranslationY(0f);
+ mFrontWrapper.getItem().setControlsEnabled(true);
+ announceForAccessibility(mFrontWrapper.getItem().getAccessibilityText());
+ }
+
+ @Override
+ int getAnimationType() {
+ return InfoBarAnimationListener.ANIMATION_TYPE_SWAP;
+ }
+ }
+
+ /**
+ * Controls whether infobars fill the full available width, or whether they "float" in the
+ * middle of the available space. The latter case happens if the available space is wider than
+ * the max width allowed for infobars.
+ *
+ * Also handles the shadows on the sides of the infobars in floating mode. The side shadows are
+ * separate views -- rather than being part of each InfoBarWrapper -- to avoid a double-shadow
+ * effect, which would happen during animations when two InfoBarWrappers overlap each other.
+ */
+ private static class FloatingBehavior {
+ /** The InfoBarContainerLayout. */
+ private FrameLayout mLayout;
+
+ /**
+ * The max width of the infobars. If the available space is wider than this, the infobars
+ * will switch to floating mode.
+ */
+ private final int mMaxWidth;
+
+ /** The width of the left and right shadows. */
+ private final int mShadowWidth;
+
+ /** Whether the layout is currently floating. */
+ private boolean mIsFloating;
+
+ /** The shadows that appear on the sides of the infobars in floating mode. */
+ private View mLeftShadowView;
+ private View mRightShadowView;
+
+ FloatingBehavior(FrameLayout layout) {
+ mLayout = layout;
+ Resources res = mLayout.getContext().getResources();
+ mMaxWidth = res.getDimensionPixelSize(R.dimen.infobar_max_width);
+ mShadowWidth = res.getDimensionPixelSize(R.dimen.infobar_shadow_width);
+ }
+
+ /**
+ * This should be called in onMeasure() before super.onMeasure(). The return value is a new
+ * widthMeasureSpec that should be passed to super.onMeasure().
+ */
+ int beforeOnMeasure(int widthMeasureSpec) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ boolean isFloating = width > mMaxWidth;
+ if (isFloating != mIsFloating) {
+ mIsFloating = isFloating;
+ onIsFloatingChanged();
+ }
+
+ if (isFloating) {
+ int mode = MeasureSpec.getMode(widthMeasureSpec);
+ width = Math.min(width, mMaxWidth + 2 * mShadowWidth);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, mode);
+ }
+ return widthMeasureSpec;
+ }
+
+ /**
+ * This should be called in onMeasure() after super.onMeasure().
+ */
+ void afterOnMeasure(int measuredHeight) {
+ if (!mIsFloating) return;
+ // Measure side shadows to match the parent view's height.
+ int widthSpec = MeasureSpec.makeMeasureSpec(mShadowWidth, MeasureSpec.EXACTLY);
+ int heightSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY);
+ mLeftShadowView.measure(widthSpec, heightSpec);
+ mRightShadowView.measure(widthSpec, heightSpec);
+ }
+
+ /**
+ * This should be called whenever the Y-position of an infobar changes.
+ */
+ void updateShadowPosition() {
+ if (!mIsFloating) return;
+ float minY = mLayout.getHeight();
+ int childCount = mLayout.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mLayout.getChildAt(i);
+ if (child != mLeftShadowView && child != mRightShadowView) {
+ minY = Math.min(minY, child.getY());
+ }
+ }
+ mLeftShadowView.setY(minY);
+ mRightShadowView.setY(minY);
+ }
+
+ private void onIsFloatingChanged() {
+ if (mIsFloating) {
+ initShadowViews();
+ mLayout.setPadding(mShadowWidth, 0, mShadowWidth, 0);
+ mLayout.setClipToPadding(false);
+ mLayout.addView(mLeftShadowView);
+ mLayout.addView(mRightShadowView);
+ } else {
+ mLayout.setPadding(0, 0, 0, 0);
+ mLayout.removeView(mLeftShadowView);
+ mLayout.removeView(mRightShadowView);
+ }
+ }
+
+ @SuppressLint("RtlHardcoded")
+ private void initShadowViews() {
+ if (mLeftShadowView != null) return;
+
+ mLeftShadowView = new View(mLayout.getContext());
+ mLeftShadowView.setBackgroundResource(R.drawable.infobar_shadow_left);
+ LayoutParams leftLp = new FrameLayout.LayoutParams(0, 0, Gravity.LEFT);
+ leftLp.leftMargin = -mShadowWidth;
+ mLeftShadowView.setLayoutParams(leftLp);
+
+ mRightShadowView = new View(mLayout.getContext());
+ mRightShadowView.setBackgroundResource(R.drawable.infobar_shadow_left);
+ LayoutParams rightLp = new FrameLayout.LayoutParams(0, 0, Gravity.RIGHT);
+ rightLp.rightMargin = -mShadowWidth;
+ mRightShadowView.setScaleX(-1f);
+ mRightShadowView.setLayoutParams(rightLp);
+ }
+ }
+
+ /**
+ * The height of back infobars, i.e. the distance between the top of the front infobar and the
+ * top of the next infobar back.
+ */
+ private final int mBackInfobarHeight;
+
+ /**
+ * All the Items, in front to back order.
+ * This list is updated immediately when addInfoBar(), removeInfoBar(), and swapInfoBar() are
+ * called; so during animations, it does *not* match the currently visible views.
+ */
+ private final ArrayList<InfoBarUiItem> mItems = new ArrayList<>();
+
+ /**
+ * The currently visible InfoBarWrappers, in front to back order.
+ */
+ private final ArrayList<InfoBarWrapper> mInfoBarWrappers = new ArrayList<>();
+
+ /** A observer that is notified when animations finish. */
+ private final InfoBarAnimationListener mAnimationListener;
+
+ /** The current animation, or null if no animation is happening currently. */
+ private InfoBarAnimation mAnimation;
+
+ private FloatingBehavior mFloatingBehavior;
+
+ /** The runnable to make infobar container fully visible. */
+ private Runnable mMakeContainerVisibleRunnable;
+
+ /**
+ * Determines whether any animations need to run in order to make the visible views match the
+ * current list of Items in mItems. If so, kicks off the next animation that's needed.
+ */
+ private void processPendingAnimations() {
+ // If an animation is running, wait until it finishes before beginning the next animation.
+ if (mAnimation != null) return;
+
+ // The steps below are ordered to minimize movement during animations. In particular,
+ // removals happen before additions or swaps, and changes are made to back infobars before
+ // front infobars.
+
+ // First, remove any infobars that are no longer in mItems, if any. Check the back infobars
+ // before the front.
+ for (int i = mInfoBarWrappers.size() - 1; i >= 0; i--) {
+ InfoBarUiItem visibleItem = mInfoBarWrappers.get(i).getItem();
+ if (!mItems.contains(visibleItem)) {
+ if (i == 0 && mInfoBarWrappers.size() >= 2) {
+ // Remove the front infobar and reveal the second-to-front infobar.
+ runAnimation(new FrontInfoBarDisappearingAndRevealingAnimation());
+ return;
+
+ } else {
+ // Move the infobar to the very back if it's not already there.
+ InfoBarWrapper wrapper = mInfoBarWrappers.get(i);
+ if (i != mInfoBarWrappers.size() - 1) {
+ removeWrapper(wrapper);
+ addWrapper(wrapper);
+ }
+
+ // Remove the backmost infobar (which may be the front infobar).
+ runAnimation(new InfoBarDisappearingAnimation());
+ return;
+ }
+ }
+ }
+
+ // Second, run swap animation on front infobar if needed.
+ if (!mInfoBarWrappers.isEmpty()) {
+ InfoBarUiItem frontItem = mInfoBarWrappers.get(0).getItem();
+ View frontContents = mInfoBarWrappers.get(0).getChildAt(0);
+ if (frontContents != frontItem.getView()) {
+ runAnimation(new FrontInfoBarSwapContentsAnimation());
+ return;
+ }
+ }
+
+ // Third, check if we should add any infobars in front of visible infobars. This can happen
+ // if an infobar has been inserted into mItems, in front of the currently visible item. To
+ // detect this the items at the beginning of mItems are compared against the first item in
+ // mInfoBarWrappers.
+ if (!mInfoBarWrappers.isEmpty()) {
+ // Find the infobar with the highest index that isn't currently being shown.
+ InfoBarUiItem currentVisibleItem = mInfoBarWrappers.get(0).getItem();
+ InfoBarUiItem itemToInsert = null;
+ for (int checkIndex = 0; checkIndex < mItems.size(); checkIndex++) {
+ if (mItems.get(checkIndex) == currentVisibleItem) {
+ // There are no remaining infobars that can possibly override the
+ // currently displayed one.
+ break;
+ } else {
+ // Found an infobar that isn't being displayed yet. Track it so that
+ // it can be animated in.
+ itemToInsert = mItems.get(checkIndex);
+ }
+ }
+ if (itemToInsert != null) {
+ runAnimation(new FrontInfoBarAppearingAnimation(itemToInsert));
+ return;
+ }
+ }
+
+ // Fourth, check if we should add any infobars at the back.
+ int desiredChildCount = Math.min(mItems.size(), MAX_STACK_DEPTH);
+ if (mInfoBarWrappers.size() < desiredChildCount) {
+ InfoBarUiItem itemToShow = mItems.get(mInfoBarWrappers.size());
+ runAnimation(mInfoBarWrappers.isEmpty()
+ ? new FirstInfoBarAppearingAnimation(itemToShow)
+ : new BackInfoBarAppearingAnimation(itemToShow));
+ return;
+ }
+
+ // Fifth, now that we've stabilized, let listeners know that we have no more animations.
+ InfoBarUiItem frontItem =
+ mInfoBarWrappers.size() > 0 ? mInfoBarWrappers.get(0).getItem() : null;
+ mAnimationListener.notifyAllAnimationsFinished(frontItem);
+ }
+
+ private void runAnimation(InfoBarAnimation animation) {
+ mAnimation = animation;
+ mAnimation.prepareAnimation();
+ if (isLayoutRequested()) {
+ // onLayout() will call mAnimation.start().
+ } else {
+ mAnimation.start();
+ }
+ }
+
+ private void addWrapper(InfoBarWrapper wrapper) {
+ addView(wrapper, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ mInfoBarWrappers.add(wrapper);
+ updateLayoutParams();
+ }
+
+ private void addWrapperToFront(InfoBarWrapper wrapper) {
+ addView(wrapper, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ mInfoBarWrappers.add(0, wrapper);
+ updateLayoutParams();
+ }
+
+ private void removeWrapper(InfoBarWrapper wrapper) {
+ removeView(wrapper);
+ mInfoBarWrappers.remove(wrapper);
+ updateLayoutParams();
+ }
+
+ private void updateLayoutParams() {
+ // Stagger the top margins so the back infobars peek out a bit.
+ int childCount = mInfoBarWrappers.size();
+ for (int i = 0; i < childCount; i++) {
+ View child = mInfoBarWrappers.get(i);
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.topMargin = (childCount - 1 - i) * mBackInfobarHeight;
+ child.setLayoutParams(lp);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ widthMeasureSpec = mFloatingBehavior.beforeOnMeasure(widthMeasureSpec);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mFloatingBehavior.afterOnMeasure(getMeasuredHeight());
+ }
+
+ @Override
+ public void announceForAccessibility(CharSequence text) {
+ if (TextUtils.isEmpty(text)) return;
+ super.announceForAccessibility(text);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mFloatingBehavior.updateShadowPosition();
+
+ // Animations start after a layout has completed, at which point all views are guaranteed
+ // to have valid sizes and positions.
+ if (mAnimation != null && !mAnimation.isStarted()) {
+ mAnimation.start();
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Trap any attempts to fiddle with the infobars while we're animating.
+ return super.onInterceptTouchEvent(ev) || mAnimation != null
+ || (!mInfoBarWrappers.isEmpty()
+ && !mInfoBarWrappers.get(0).getItem().areControlsEnabled());
+ }
+
+ @Override
+ @SuppressLint("ClickableViewAccessibility")
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ // Consume all touch events so they do not reach the ContentView.
+ return true;
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ super.onHoverEvent(event);
+ // Consume all hover events so they do not reach the ContentView. In touch exploration mode,
+ // this prevents the user from interacting with the part of the ContentView behind the
+ // infobars. http://crbug.com/430701
+ return true;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java
new file mode 100644
index 00000000000..553608310f2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java
@@ -0,0 +1,257 @@
+// Copyright 2013 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.MathUtils;
+import org.chromium.ui.display.DisplayAndroid;
+import org.chromium.ui.display.DisplayUtil;
+
+/**
+ * The {@link View} for the {@link InfoBarContainer}.
+ */
+public class InfoBarContainerView extends SwipableOverlayView {
+ /**
+ * Observes container view changes.
+ */
+ public interface ContainerViewObserver extends InfoBarContainer.InfoBarAnimationListener {
+ /**
+ * Called when the height of shown content changed.
+ * @param shownFraction The ratio of height of shown content to the height of the container
+ * view.
+ */
+ void onShownRatioChanged(float shownFraction);
+ }
+
+ /** Top margin, including the toolbar and tabstrip height and 48dp of web contents. */
+ private static final int TOP_MARGIN_PHONE_DP = 104;
+ private static final int TOP_MARGIN_TABLET_DP = 144;
+
+ /** Length of the animation to fade the InfoBarContainer back into View. */
+ private static final long REATTACH_FADE_IN_MS = 250;
+
+ /** Whether or not the InfoBarContainer is allowed to hide when the user scrolls. */
+ private static boolean sIsAllowedToAutoHide = true;
+
+ private final ContainerViewObserver mContainerViewObserver;
+ private final InfoBarContainerLayout mLayout;
+
+ /** Parent view that contains the InfoBarContainerLayout. */
+ private ViewGroup mParentView;
+
+ private TabImpl mTab;
+
+ /** Animation used to snap the container to the nearest state if scroll direction changes. */
+ private Animator mScrollDirectionChangeAnimation;
+
+ /** Whether or not the current scroll is downward. */
+ private boolean mIsScrollingDownward;
+
+ /** Tracks the previous event's scroll offset to determine if a scroll is up or down. */
+ private int mLastScrollOffsetY;
+
+ /**
+ * @param context The {@link Context} that this view is attached to.
+ * @param containerViewObserver The {@link ContainerViewObserver} that gets notified on
+ * container view changes.
+ * @param isTablet Whether this view is displayed on tablet or not.
+ */
+ InfoBarContainerView(@NonNull Context context,
+ @NonNull ContainerViewObserver containerViewObserver, TabImpl tab, boolean isTablet) {
+ super(context, null);
+ mTab = tab;
+ mContainerViewObserver = containerViewObserver;
+
+ // TODO(newt): move this workaround into the infobar views if/when they're scrollable.
+ // Workaround for http://crbug.com/407149. See explanation in onMeasure() below.
+ setVerticalScrollBarEnabled(false);
+
+ updateLayoutParams(context, isTablet);
+
+ Runnable makeContainerVisibleRunnable = () -> runUpEventAnimation(true);
+ mLayout = new InfoBarContainerLayout(context, makeContainerVisibleRunnable,
+ new InfoBarContainer.InfoBarAnimationListener() {
+ @Override
+ public void notifyAnimationFinished(int animationType) {
+ mContainerViewObserver.notifyAnimationFinished(animationType);
+ }
+
+ @Override
+ public void notifyAllAnimationsFinished(InfoBarUiItem frontInfoBar) {
+ mContainerViewObserver.notifyAllAnimationsFinished(frontInfoBar);
+ }
+ });
+
+ addView(mLayout,
+ new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL));
+ }
+
+ void destroy() {
+ removeFromParentView();
+ mTab = null;
+ }
+
+ // SwipableOverlayView implementation.
+ @Override
+ @VisibleForTesting
+ public boolean isAllowedToAutoHide() {
+ return sIsAllowedToAutoHide;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (getVisibility() != View.GONE) {
+ setVisibility(VISIBLE);
+ setAlpha(0f);
+ animate().alpha(1f).setDuration(REATTACH_FADE_IN_MS);
+ }
+ }
+
+ @Override
+ protected void runUpEventAnimation(boolean visible) {
+ if (mScrollDirectionChangeAnimation != null) mScrollDirectionChangeAnimation.cancel();
+ super.runUpEventAnimation(visible);
+ }
+
+ @Override
+ protected boolean isIndependentlyAnimating() {
+ return mScrollDirectionChangeAnimation != null;
+ }
+
+ // View implementation.
+ @Override
+ public void setTranslationY(float translationY) {
+ int contentHeightDelta = mTab != null
+ ? mTab.getBrowser().getViewController().getBottomContentHeightDelta()
+ : 0;
+
+ // Push the infobar container up by any delta caused by the bottom toolbar while ensuring
+ // that it does not ascend beyond the top of the bottom toolbar nor descend beyond its own
+ // height.
+ float newTranslationY = MathUtils.clamp(
+ translationY - contentHeightDelta, -contentHeightDelta, getHeight());
+
+ super.setTranslationY(newTranslationY);
+
+ float shownFraction = 0;
+ if (getHeight() > 0) {
+ shownFraction = contentHeightDelta > 0 ? 1f : 1f - (translationY / getHeight());
+ }
+ mContainerViewObserver.onShownRatioChanged(shownFraction);
+ }
+
+ /**
+ * Sets whether the InfoBarContainer is allowed to auto-hide when the user scrolls the page.
+ * Expected to be called when Touch Exploration is enabled.
+ * @param isAllowed Whether auto-hiding is allowed.
+ */
+ public static void setIsAllowedToAutoHide(boolean isAllowed) {
+ sIsAllowedToAutoHide = isAllowed;
+ }
+
+ /**
+ * Notifies that an infobar's View ({@link InfoBar#getView}) has changed. If the infobar is
+ * visible, a view swapping animation will be run.
+ */
+ void notifyInfoBarViewChanged() {
+ mLayout.notifyInfoBarViewChanged();
+ }
+
+ /**
+ * Sets the parent {@link ViewGroup} that contains the {@link InfoBarContainer}.
+ */
+ void setParentView(ViewGroup parent) {
+ mParentView = parent;
+ // Don't attach the container to the new parent if it is not previously attached.
+ if (removeFromParentView()) addToParentView();
+ }
+
+ /**
+ * Adds this class to the parent view {@link #mParentView}.
+ */
+ void addToParentView() {
+ // If mTab is null, destroy() was called. This should not be added after destroyed.
+ assert mTab != null;
+ super.addToParentView(mParentView,
+ mTab.getBrowser().getViewController().getDesiredInfoBarContainerViewIndex());
+ }
+
+ /**
+ * Adds an {@link InfoBar} to the layout.
+ * @param infoBar The {@link InfoBar} to be added.
+ */
+ void addInfoBar(InfoBar infoBar) {
+ infoBar.createView();
+ mLayout.addInfoBar(infoBar);
+ }
+
+ /**
+ * Removes an {@link InfoBar} from the layout.
+ * @param infoBar The {@link InfoBar} to be removed.
+ */
+ void removeInfoBar(InfoBar infoBar) {
+ mLayout.removeInfoBar(infoBar);
+ }
+
+ /**
+ * Hides or stops hiding this View.
+ * @param isHidden Whether this View is should be hidden.
+ */
+ void setHidden(boolean isHidden) {
+ setVisibility(isHidden ? View.GONE : View.VISIBLE);
+ }
+
+ /**
+ * Run an animation when the scrolling direction of a gesture has changed (this does not mean
+ * the gesture has ended).
+ * @param visible Whether or not the view should be visible.
+ */
+ private void runDirectionChangeAnimation(boolean visible) {
+ mScrollDirectionChangeAnimation = createVerticalSnapAnimation(visible);
+ mScrollDirectionChangeAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrollDirectionChangeAnimation = null;
+ }
+ });
+ mScrollDirectionChangeAnimation.start();
+ }
+
+ @Override
+ // Ensure that this view's custom layout params are passed when adding it to its parent.
+ public ViewGroup.MarginLayoutParams createLayoutParams() {
+ return (ViewGroup.MarginLayoutParams) getLayoutParams();
+ }
+
+ private void updateLayoutParams(Context context, boolean isTablet) {
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ int topMarginDp = isTablet ? TOP_MARGIN_TABLET_DP : TOP_MARGIN_PHONE_DP;
+ lp.topMargin = DisplayUtil.dpToPx(DisplayAndroid.getNonMultiDisplay(context), topMarginDp);
+ setLayoutParams(lp);
+ }
+
+ /**
+ * Returns true if any animations are pending or in progress.
+ */
+ @VisibleForTesting
+ public boolean isAnimating() {
+ return mLayout.isAnimating();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java
new file mode 100644
index 00000000000..5a653d069c3
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java
@@ -0,0 +1,69 @@
+// Copyright 2020 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.View;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An interface for items that can be added to an InfoBarContainerLayout.
+ */
+public interface InfoBarUiItem {
+ // The infobar priority.
+ @IntDef({InfoBarPriority.CRITICAL, InfoBarPriority.USER_TRIGGERED,
+ InfoBarPriority.PAGE_TRIGGERED, InfoBarPriority.BACKGROUND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InfoBarPriority {
+ int CRITICAL = 0;
+ int USER_TRIGGERED = 1;
+ int PAGE_TRIGGERED = 2;
+ int BACKGROUND = 3;
+ }
+
+ /**
+ * Returns the View that represents this infobar. This should have no background or borders;
+ * a background and shadow will be added by a wrapper view.
+ */
+ View getView();
+
+ /**
+ * Returns whether controls for this View should be clickable. If false, all input events on
+ * this item will be ignored.
+ */
+ boolean areControlsEnabled();
+
+ /**
+ * Sets whether or not controls for this View should be clickable. This does not affect the
+ * visual state of the infobar.
+ * @param state If false, all input events on this Item will be ignored.
+ */
+ void setControlsEnabled(boolean state);
+
+ /**
+ * Returns the accessibility text to announce when this infobar is first shown.
+ */
+ CharSequence getAccessibilityText();
+
+ /**
+ * Returns the priority of an infobar. High priority infobar is shown in front of low
+ * priority infobar. If infobars have the same priorities, the most recently added one
+ * is shown behind previous ones.
+ *
+ */
+ int getPriority();
+
+ /**
+ * Returns the type of infobar, as best as can be determined at this time. See
+ * components/infobars/core/infobar_delegate.h.
+ */
+ @InfoBarIdentifier
+ int getInfoBarIdentifier();
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java
new file mode 100644
index 00000000000..8a574254a96
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * Layout that holds an infobar's contents and provides a background color and a top shadow.
+ */
+class InfoBarWrapper extends FrameLayout {
+ private final InfoBarUiItem mItem;
+
+ /**
+ * Constructor for inflating from Java.
+ */
+ InfoBarWrapper(Context context, InfoBarUiItem item) {
+ super(context);
+ mItem = item;
+ Resources res = context.getResources();
+ int peekingHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height);
+ int shadowHeight = res.getDimensionPixelSize(R.dimen.infobar_shadow_height);
+ setMinimumHeight(peekingHeight + shadowHeight);
+
+ // setBackgroundResource() changes the padding, so call setPadding() second.
+ setBackgroundResource(R.drawable.weblayer_infobar_wrapper_bg);
+ setPadding(0, shadowHeight, 0, 0);
+ }
+
+ InfoBarUiItem getItem() {
+ return mItem;
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ child.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.TOP));
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/IntentUtils.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/IntentUtils.java
new file mode 100644
index 00000000000..5d2dfec5c69
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/IntentUtils.java
@@ -0,0 +1,48 @@
+// Copyright 2020 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.Intent;
+import android.os.RemoteException;
+import android.util.AndroidRuntimeException;
+
+/** A utility class for creating and handling common intents. */
+public class IntentUtils {
+ private static final String sExtraTabId = "TAB_ID";
+ private static final String sActivateTabAction =
+ "org.chromium.weblayer.intent_utils.ACTIVATE_TAB";
+
+ /**
+ * Handles an intent generated by this class.
+ * @return true if the intent was handled, or false if the intent wasn't generated by this
+ * class.
+ */
+ public static boolean handleIntent(Intent intent) {
+ if (!intent.getAction().equals(sActivateTabAction)) return false;
+
+ int tabId = intent.getIntExtra(sExtraTabId, -1);
+ TabImpl tab = TabImpl.getTabById(tabId);
+ if (tab == null) return true;
+
+ try {
+ tab.getClient().bringTabToFront();
+ } catch (RemoteException e) {
+ throw new AndroidRuntimeException(e);
+ }
+ return true;
+ }
+
+ /**
+ * Creates an intent to bring a tab to the foreground.
+ * This intent should also bring the app to the foreground.
+ * @param tabId the identifier for the tab.
+ */
+ public static Intent createBringTabToFrontIntent(int tabId) {
+ Intent intent = WebLayerImpl.createIntent();
+ intent.putExtra(sExtraTabId, tabId);
+ intent.setAction(sActivateTabAction);
+ return intent;
+ }
+};
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java
new file mode 100644
index 00000000000..a36125ace70
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java
@@ -0,0 +1,140 @@
+// Copyright 2020 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.app.Service;
+import android.content.Intent;
+import android.support.v4.media.session.MediaSessionCompat;
+
+import org.chromium.components.browser_ui.media.MediaNotificationController;
+import org.chromium.components.browser_ui.media.MediaNotificationInfo;
+import org.chromium.components.browser_ui.media.MediaSessionHelper;
+import org.chromium.components.browser_ui.notifications.ChromeNotification;
+import org.chromium.components.browser_ui.notifications.ChromeNotificationBuilder;
+import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils;
+import org.chromium.components.browser_ui.notifications.NotificationMetadata;
+
+/**
+ * A glue class for MediaSession.
+ * This class defines delegates that provide WebLayer-specific behavior to shared MediaSession code.
+ * It also manages the lifetime of {@link MediaNotificationController} and the {@link Service}
+ * associated with the notification.
+ */
+class MediaSessionManager {
+ // This is a singleton because there's only at most one MediaSession active at a time.
+ @SuppressLint("StaticFieldLeak")
+ static MediaNotificationController sController;
+
+ private static int sNotificationId = 0;
+
+ static void serviceStarted(Service service, Intent intent) {
+ if (sController != null && sController.processIntent(service, intent)) return;
+
+ // The service has been started with startForegroundService() but the
+ // notification hasn't been shown. See similar logic in {@link
+ // ChromeMediaNotificationControllerDelegate}.
+ MediaNotificationController.finishStartingForegroundServiceOnO(
+ service, createChromeNotificationBuilder().buildChromeNotification());
+ // Call stopForeground to guarantee Android unset the foreground bit.
+ ForegroundServiceUtils.getInstance().stopForeground(
+ service, Service.STOP_FOREGROUND_REMOVE);
+ service.stopSelf();
+ }
+
+ static void serviceDestroyed() {
+ if (sController != null) sController.onServiceDestroyed();
+ sController = null;
+ }
+
+ static MediaSessionHelper.Delegate createMediaSessionHelperDelegate(int tabId) {
+ return new MediaSessionHelper.Delegate() {
+ @Override
+ public Intent createBringTabToFrontIntent() {
+ return IntentUtils.createBringTabToFrontIntent(tabId);
+ }
+
+ @Override
+ public boolean fetchLargeFaviconImage() {
+ // TODO(crbug.com/1076463): WebLayer doesn't support favicons.
+ return false;
+ }
+
+ @Override
+ public MediaNotificationInfo.Builder createMediaNotificationInfoBuilder() {
+ ensureNotificationId();
+ return new MediaNotificationInfo.Builder().setInstanceId(tabId).setId(
+ sNotificationId);
+ }
+
+ @Override
+ public void showMediaNotification(MediaNotificationInfo notificationInfo) {
+ assert notificationInfo.id == sNotificationId;
+ if (sController == null) {
+ sController = new MediaNotificationController(
+ new WebLayerMediaNotificationControllerDelegate());
+ }
+ sController.mThrottler.queueNotification(notificationInfo);
+ }
+
+ @Override
+ public void hideMediaNotification() {
+ if (sController != null) sController.hideNotification(tabId);
+ }
+
+ @Override
+ public void activateAndroidMediaSession() {
+ if (sController != null) sController.activateAndroidMediaSession(tabId);
+ }
+ };
+ }
+
+ private static class WebLayerMediaNotificationControllerDelegate
+ implements MediaNotificationController.Delegate {
+ @Override
+ public Intent createServiceIntent() {
+ return WebLayerImpl.createMediaSessionServiceIntent();
+ }
+
+ @Override
+ public String getAppName() {
+ return WebLayerImpl.getClientApplicationName();
+ }
+
+ @Override
+ public String getNotificationGroupName() {
+ return "org.chromium.weblayer.MediaSession";
+ }
+
+ @Override
+ public ChromeNotificationBuilder createChromeNotificationBuilder() {
+ return MediaSessionManager.createChromeNotificationBuilder();
+ }
+
+ @Override
+ public void onMediaSessionUpdated(MediaSessionCompat session) {
+ // This is only relevant when casting.
+ }
+
+ @Override
+ public void logNotificationShown(ChromeNotification notification) {}
+ }
+
+ private static ChromeNotificationBuilder createChromeNotificationBuilder() {
+ ensureNotificationId();
+
+ // Only the null tag will work as expected, because {@link Service#startForeground()} only
+ // takes an ID and no tag. If we pass a tag here, then the notification that's used to
+ // display a paused state (no foreground service) will not be identified as the same one
+ // that's used with the foreground service.
+ return WebLayerNotificationBuilder.create(
+ WebLayerNotificationChannels.ChannelId.MEDIA_PLAYBACK,
+ new NotificationMetadata(0, null /*notificationTag*/, sNotificationId));
+ }
+
+ private static void ensureNotificationId() {
+ if (sNotificationId == 0) sNotificationId = WebLayerImpl.getMediaSessionNotificationId();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java
index 446af44ea47..90925f10df5 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java
@@ -21,7 +21,6 @@ import org.chromium.components.browser_ui.notifications.NotificationManagerProxy
import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
import org.chromium.components.browser_ui.notifications.NotificationMetadata;
import org.chromium.components.browser_ui.notifications.PendingIntentProvider;
-import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
import org.chromium.components.webrtc.MediaCaptureNotificationUtil;
import org.chromium.components.webrtc.MediaCaptureNotificationUtil.MediaType;
import org.chromium.content_public.browser.WebContents;
@@ -65,6 +64,7 @@ public class MediaStreamManager {
/**
* @return a string that prefixes all intents that can be handled by {@link forwardIntent}.
+ * @Deprecated in M85+, this class does not handle intents. Remove in M88.
*/
public static String getIntentPrefix() {
return WEBRTC_PREFIX;
@@ -73,6 +73,7 @@ public class MediaStreamManager {
/**
* Handles an intent coming from a media streaming notification.
* @param intent the intent which was previously posted via {@link update}.
+ * @Deprecated in M85+, this class does not handle intents. Remove in M88.
*/
public static void forwardIntent(Intent intent) {
assert intent.getAction().equals(ACTIVATE_TAB_INTENT);
@@ -208,28 +209,28 @@ public class MediaStreamManager {
}
Context appContext = ContextUtils.getApplicationContext();
- Intent intent = WebLayerImpl.createIntent();
- intent.putExtra(EXTRA_TAB_ID, mNotificationId);
- intent.setAction(ACTIVATE_TAB_INTENT);
+ Intent intent = null;
+ if (WebLayerFactoryImpl.getClientMajorVersion() >= 85) {
+ intent = IntentUtils.createBringTabToFrontIntent(mNotificationId);
+ } else {
+ intent = WebLayerImpl.createIntent();
+ intent.putExtra(EXTRA_TAB_ID, mNotificationId);
+ intent.setAction(ACTIVATE_TAB_INTENT);
+ }
PendingIntentProvider contentIntent =
PendingIntentProvider.getBroadcast(appContext, mNotificationId, intent, 0);
int mediaType = audio && video ? MediaType.AUDIO_AND_VIDEO
: audio ? MediaType.AUDIO_ONLY : MediaType.VIDEO_ONLY;
- NotificationManagerProxy notificationManagerProxy = getNotificationManager();
- ChannelsInitializer channelsInitializer = new ChannelsInitializer(notificationManagerProxy,
- WebLayerNotificationChannels.getInstance(), appContext.getResources());
-
// TODO(crbug/1076098): don't pass a URL in incognito.
ChromeNotification notification = MediaCaptureNotificationUtil.createNotification(
- new WebLayerNotificationBuilder(appContext,
+ WebLayerNotificationBuilder.create(
WebLayerNotificationChannels.ChannelId.WEBRTC_CAM_AND_MIC,
- channelsInitializer,
new NotificationMetadata(0, AV_STREAM_TAG, mNotificationId)),
mediaType, mTab.getWebContents().getVisibleUrl().getSpec(),
WebLayerImpl.getClientApplicationName(), contentIntent, null /*stopIntent*/);
- notificationManagerProxy.notify(notification);
+ getNotificationManager().notify(notification);
updateActiveNotifications(true);
notifyClient(audio, video);
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java
new file mode 100644
index 00000000000..d124792cd0d
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java
@@ -0,0 +1,28 @@
+// Copyright 2020 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.annotations.CalledByNative;
+import org.chromium.content_public.browser.InterfaceRegistrar;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.services.service_manager.InterfaceRegistry;
+import org.chromium.webshare.mojom.ShareService;
+
+/**
+ * Registers Java implementations of mojo interfaces.
+ */
+class MojoInterfaceRegistrar {
+ @CalledByNative
+ private static void registerMojoInterfaces() {
+ InterfaceRegistrar.Registry.addWebContentsRegistrar(new WebContentsInterfaceRegistrar());
+ }
+
+ private static class WebContentsInterfaceRegistrar implements InterfaceRegistrar<WebContents> {
+ @Override
+ public void registerInterfaces(InterfaceRegistry registry, final WebContents webContents) {
+ registry.addInterface(ShareService.MANAGER, new WebShareServiceFactory(webContents));
+ }
+ }
+}
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 c44b3b66031..3957ed46557 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
@@ -113,6 +113,13 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
mNativeNavigationController, index);
}
+ @Override
+ public boolean isNavigationEntrySkippable(int index) {
+ StrictModeWorkaround.apply();
+ return NavigationControllerImplJni.get().isNavigationEntrySkippable(
+ mNativeNavigationController, index);
+ }
+
@CalledByNative
private NavigationImpl createNavigation(long nativeNavigationImpl) {
return new NavigationImpl(mNavigationControllerClient, nativeNavigationImpl);
@@ -159,6 +166,12 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
mNavigationControllerClient.onFirstContentfulPaint();
}
+ @CalledByNative
+ private void onOldPageNoLongerRendered(String uri) throws RemoteException {
+ if (WebLayerFactoryImpl.getClientMajorVersion() < 85) return;
+ mNavigationControllerClient.onOldPageNoLongerRendered(uri);
+ }
+
@NativeMethods
interface Natives {
void setNavigationControllerImpl(
@@ -178,5 +191,6 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
int getNavigationListCurrentIndex(long nativeNavigationControllerImpl);
String getNavigationEntryDisplayUri(long nativeNavigationControllerImpl, int index);
String getNavigationEntryTitle(long nativeNavigationControllerImpl, int index);
+ boolean isNavigationEntrySkippable(long nativeNavigationControllerImpl, int index);
}
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
index c5d665b7c08..25645acdc76 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
@@ -48,12 +48,10 @@ public final class NewTabCallbackProxy {
}
@CalledByNative
- public void onNewTab(long nativeTab, @ImplNewTabType int mode) throws RemoteException {
+ public void onNewTab(TabImpl tab, @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);
+ assert mTab.getBrowser().equals(tab.getBrowser());
mTab.getClient().onNewTab(tab.getId(), mode);
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java
index 687be42fe1a..314b6b7ff29 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java
@@ -7,10 +7,15 @@ package org.chromium.weblayer_private;
import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
+
import org.chromium.base.StrictModeContext;
import org.chromium.base.supplier.Supplier;
+import org.chromium.components.content_settings.CookieControlsBridge;
+import org.chromium.components.content_settings.CookieControlsObserver;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.page_info.PageInfoControllerDelegate;
+import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.url.GURL;
import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper;
@@ -20,18 +25,27 @@ import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper;
*/
public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate {
private final Context mContext;
+ private final WebContents mWebContents;
private final String mProfileName;
- public PageInfoControllerDelegateImpl(Context context, String profileName, GURL url,
- Supplier<ModalDialogManager> modalDialogManager) {
+ static PageInfoControllerDelegateImpl create(WebContents webContents) {
+ TabImpl tab = TabImpl.fromWebContents(webContents);
+ assert tab != null;
+ return new PageInfoControllerDelegateImpl(tab.getBrowser().getContext(), webContents,
+ tab.getProfile(), tab.getBrowser().getWindowAndroid()::getModalDialogManager);
+ }
+
+ private PageInfoControllerDelegateImpl(Context context, WebContents webContents,
+ ProfileImpl profile, Supplier<ModalDialogManager> modalDialogManager) {
super(modalDialogManager, new AutocompleteSchemeClassifierImpl(),
/** vrHandler= */ null,
/** isSiteSettingsAvailable= */
- UrlConstants.HTTP_SCHEME.equals(url.getScheme())
- || UrlConstants.HTTPS_SCHEME.equals(url.getScheme()),
- /** cookieControlsShown= */ false);
+ isHttpOrHttps(webContents.getVisibleUrl()),
+ /** cookieControlsShown= */
+ CookieControlsBridge.isCookieControlsEnabled(profile));
mContext = context;
- mProfileName = profileName;
+ mWebContents = webContents;
+ mProfileName = profile.getName();
}
/**
@@ -47,4 +61,18 @@ public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate {
mContext.startActivity(intent);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @NonNull
+ public CookieControlsBridge createCookieControlsBridge(CookieControlsObserver observer) {
+ return new CookieControlsBridge(observer, mWebContents, null);
+ }
+
+ private static boolean isHttpOrHttps(GURL url) {
+ String scheme = url.getScheme();
+ return UrlConstants.HTTP_SCHEME.equals(scheme) || UrlConstants.HTTPS_SCHEME.equals(scheme);
+ }
}
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 ba5cc8c56dd..c359dc3513d 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -5,6 +5,7 @@
package org.chromium.weblayer_private;
import android.content.Intent;
+import android.text.TextUtils;
import android.webkit.ValueCallback;
import androidx.annotation.NonNull;
@@ -25,7 +26,10 @@ import org.chromium.weblayer_private.interfaces.SettingType;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Implementation of IProfile.
@@ -168,6 +172,40 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa
return mCookieManager;
}
+ @Override
+ public void getBrowserPersistenceIds(@NonNull IObjectWrapper callback) {
+ StrictModeWorkaround.apply();
+ checkNotDestroyed();
+ ValueCallback<Set<String>> valueCallback =
+ (ValueCallback<Set<String>>) ObjectWrapper.unwrap(callback, ValueCallback.class);
+ Callback<String[]> baseCallback = (String[] result) -> {
+ valueCallback.onReceiveValue(new HashSet<String>(Arrays.asList(result)));
+ };
+ ProfileImplJni.get().getBrowserPersistenceIds(mNativeProfile, baseCallback);
+ }
+
+ @Override
+ public void removeBrowserPersistenceStorage(String[] ids, @NonNull IObjectWrapper callback) {
+ StrictModeWorkaround.apply();
+ checkNotDestroyed();
+ ValueCallback<Boolean> valueCallback =
+ (ValueCallback<Boolean>) ObjectWrapper.unwrap(callback, ValueCallback.class);
+ Callback<Boolean> baseCallback = valueCallback::onReceiveValue;
+ for (String id : ids) {
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id must be non-null and non-empty");
+ }
+ }
+ ProfileImplJni.get().removeBrowserPersistenceStorage(mNativeProfile, ids, baseCallback);
+ }
+
+ @Override
+ public void prepareForPossibleCrossOriginNavigation() {
+ StrictModeWorkaround.apply();
+ checkNotDestroyed();
+ ProfileImplJni.get().prepareForPossibleCrossOriginNavigation(mNativeProfile);
+ }
+
void checkNotDestroyed() {
if (!mBeingDeleted) return;
throw new IllegalArgumentException("Profile being destroyed: " + mName);
@@ -232,5 +270,9 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa
void ensureBrowserContextInitialized(long nativeProfileImpl);
void setBooleanSetting(long nativeProfileImpl, int type, boolean value);
boolean getBooleanSetting(long nativeProfileImpl, int type);
+ void getBrowserPersistenceIds(long nativeProfileImpl, Callback<String[]> callback);
+ void removeBrowserPersistenceStorage(
+ long nativeProfileImpl, String[] ids, Callback<Boolean> callback);
+ void prepareForPossibleCrossOriginNavigation(long nativeProfileImpl);
}
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/README.md b/chromium/weblayer/browser/java/org/chromium/weblayer_private/README.md
new file mode 100644
index 00000000000..35e0aedea89
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/README.md
@@ -0,0 +1,36 @@
+# Which Context should I use?
+
+The code in this directory references different types of contexts. Please read about what each
+represents before deciding which one you should use.
+
+## Embedder's Activity Context
+
+The fragment that WebLayer is loaded in holds a reference to the activity that it is currently
+attached to. This is what's referred to by [`mEmbedderActivityContext`][link1] in BrowserImpl and
+BrowserFragmentImpl. It should be used to reference anything associated with the activity. For
+instance, embedder-specific resources, like Color resources which are resolved according to the
+theme of the embedding activity.
+
+[link1]: https://source.chromium.org/chromium/chromium/src/+/6c336f4d55231595c038756f58a9e61d416a9c8f:weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java;bpv=1;bpt=1
+
+## WebLayer's Activity Context
+
+WebLayer has a lot of resources of its own which need to be accessed by the implementation code. We
+thus wrap the embedder's activity context so that resource and assert look-ups against the wrapped
+context go to the WebView or WebLayer support APK and not the embedder's APK. This wrapped Context
+is what's returned by [`BrowserImpl.getContext()`][link2]. Use this when referencing WebLayer specific
+resources. This is expected to be the most common use case.
+
+[link2]: https://source.chromium.org/chromium/chromium/src/+/master:weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java?q=f:browserimpl%20getContext&ss=chromium%2Fchromium%2Fsrc
+
+## Embedder's Application Context
+
+Occasionally, we need the embedder's application context, as opposed to its activity context. For
+instance, fetching the current locale which applies to the entire application.
+Similar to WebLayer's Activity Context, this context is also wrapped in our implementation so we can
+reference WebLayer-specific resources. This is what's returned by
+[`ContextUtils.getApplicationContext()`][link3].
+It shouldn't be downcast to Application (or any subclass thereof) since it's wrapped in a
+ContextWrapper.
+
+[link3]: https://source.chromium.org/chromium/chromium/src/+/master:base/android/java/src/org/chromium/base/ContextUtils.java?q=f:base%2FContextUtils%20getApplicationContext()&ss=chromium%2Fchromium%2Fsrc
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java
index d30bc58aed9..072df069fb9 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java
@@ -12,15 +12,18 @@ import android.os.Handler;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.view.Window;
+import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentController;
import androidx.fragment.app.FragmentHostCallback;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
import org.chromium.components.browser_ui.site_settings.SingleCategorySettings;
import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
import org.chromium.components.browser_ui.site_settings.SiteSettings;
@@ -59,6 +62,7 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
// resource IDs.
private Context mContext;
+ private boolean mStarted;
private FragmentController mFragmentController;
/**
@@ -78,6 +82,12 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
private PassthroughFragmentActivity(SiteSettingsFragmentImpl fragmentImpl) {
mFragmentImpl = fragmentImpl;
attachBaseContext(mFragmentImpl.getWebLayerContext());
+ // This class doesn't extend AppCompatActivity, so some appcompat functionality doesn't
+ // get initialized, which leads to some appcompat widgets (like switches) rendering
+ // incorrectly. There are some resource issues with having this class extend
+ // AppCompatActivity, but until we sort those out, creating an AppCompatDelegate will
+ // perform the necessary initialization.
+ AppCompatDelegate.create(this, null);
}
@Override
@@ -182,8 +192,9 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
@Override
public LayoutInflater onGetLayoutInflater() {
- return (LayoutInflater) mFragmentImpl.getWebLayerContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
+ Context context = mFragmentImpl.getWebLayerContext();
+ return ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+ .cloneInContext(context);
}
@Override
@@ -271,6 +282,24 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
throw new RuntimeException("Failed to create Site Settings Fragment", e);
}
}
+
+ root.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // Add the shadow scroll listener here once the View is attached to the Window.
+ SiteSettingsPreferenceFragment preferenceFragment =
+ (SiteSettingsPreferenceFragment) mFragmentController
+ .getSupportFragmentManager()
+ .findFragmentByTag(FRAGMENT_TAG);
+ ViewGroup listView = preferenceFragment.getListView();
+ listView.getViewTreeObserver().addOnScrollChangedListener(
+ SettingsUtils.getShowShadowOnScrollListener(
+ listView, view.findViewById(R.id.shadow)));
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ });
return root;
}
@@ -298,7 +327,11 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
@Override
public void onStart() {
super.onStart();
- mFragmentController.dispatchActivityCreated();
+
+ if (!mStarted) {
+ mStarted = true;
+ mFragmentController.dispatchActivityCreated();
+ }
mFragmentController.noteStateNotSaved();
mFragmentController.execPendingActions();
mFragmentController.dispatchStart();
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java
new file mode 100644
index 00000000000..7f46f8afcd2
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java
@@ -0,0 +1,421 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.MathUtils;
+import org.chromium.content_public.browser.GestureListenerManager;
+import org.chromium.content_public.browser.GestureStateListenerWithScroll;
+import org.chromium.content_public.browser.WebContents;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * View that slides up from the bottom of the page and slides away as the user scrolls the page.
+ * Meant to be tacked onto the {@link org.chromium.content_public.browser.WebContents}'s view and
+ * alerted when either the page scroll position or viewport size changes.
+ *
+ * GENERAL BEHAVIOR
+ * This View is brought onto the screen by sliding upwards from the bottom of the screen. Afterward
+ * the View slides onto and off of the screen vertically as the user scrolls upwards or
+ * downwards on the page.
+ *
+ * As the scroll offset or the viewport height are updated via a scroll or fling, the difference
+ * from the initial value is used to determine the View's Y-translation. If a gesture is stopped,
+ * the View will be snapped back into the center of the screen or entirely off of the screen, based
+ * on how much of the View is visible, or where the user is currently located on the page.
+ */
+public abstract class SwipableOverlayView extends FrameLayout {
+ private static final float FULL_THRESHOLD = 0.5f;
+ private static final float VERTICAL_FLING_SHOW_THRESHOLD = 0.2f;
+ private static final float VERTICAL_FLING_HIDE_THRESHOLD = 0.9f;
+
+ @IntDef({Gesture.NONE, Gesture.SCROLLING, Gesture.FLINGING})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Gesture {
+ int NONE = 0;
+ int SCROLLING = 1;
+ int FLINGING = 2;
+ }
+
+ private static final long ANIMATION_DURATION_MS = 250;
+
+ /** Detects when the user is dragging the WebContents. */
+ private final GestureStateListenerWithScroll mGestureStateListener;
+
+ /** Listens for changes in the layout. */
+ private final View.OnLayoutChangeListener mLayoutChangeListener;
+
+ /** Interpolator used for the animation. */
+ private final Interpolator mInterpolator;
+
+ /** Tracks whether the user is scrolling or flinging. */
+ private @Gesture int mGestureState;
+
+ /** Animation currently being used to translate the View. */
+ private Animator mCurrentAnimation;
+
+ /** Used to determine when the layout has changed and the Viewport must be updated. */
+ private int mParentHeight;
+
+ /** Offset from the top of the page when the current gesture was first started. */
+ private int mInitialOffsetY;
+
+ /** How tall the View is, including its margins. */
+ private int mTotalHeight;
+
+ /** Whether or not the View ever been fully displayed. */
+ private boolean mIsBeingDisplayedForFirstTime;
+
+ /** The WebContents to which the overlay is added. */
+ private WebContents mWebContents;
+
+ /**
+ * Creates a SwipableOverlayView.
+ * @param context Context for acquiring resources.
+ * @param attrs Attributes from the XML layout inflation.
+ */
+ public SwipableOverlayView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mGestureStateListener = createGestureStateListener();
+ mGestureState = Gesture.NONE;
+ mLayoutChangeListener = createLayoutChangeListener();
+ mInterpolator = new DecelerateInterpolator(1.0f);
+
+ // We make this view 'draw' to provide a placeholder for its animations.
+ setWillNotDraw(false);
+ }
+
+ /**
+ * Set the given WebContents for scrolling changes.
+ */
+ public void setWebContents(WebContents webContents) {
+ if (mWebContents != null) {
+ GestureListenerManager.fromWebContents(mWebContents)
+ .removeListener(mGestureStateListener);
+ }
+
+ mWebContents = webContents;
+ // See comment in onLayout() as to why the listener is only attached if mTotalHeight is > 0.
+ if (mWebContents != null && mTotalHeight > 0) {
+ GestureListenerManager.fromWebContents(mWebContents).addListener(mGestureStateListener);
+ }
+ }
+
+ public WebContents getWebContents() {
+ return mWebContents;
+ }
+
+ protected void addToParentView(ViewGroup parentView, int index) {
+ if (parentView == null) return;
+ if (getParent() == null) {
+ parentView.addView(this, index, createLayoutParams());
+
+ // Listen for the layout to know when to animate the View coming onto the screen.
+ addOnLayoutChangeListener(mLayoutChangeListener);
+ }
+ }
+
+ /**
+ * Removes the SwipableOverlayView from its parent and stops monitoring the WebContents.
+ * @return Whether the View was removed from its parent.
+ */
+ public boolean removeFromParentView() {
+ if (getParent() == null) return false;
+
+ ((ViewGroup) getParent()).removeView(this);
+ removeOnLayoutChangeListener(mLayoutChangeListener);
+ return true;
+ }
+
+ /**
+ * Creates a set of LayoutParams that makes the View hug the bottom of the screen. Override it
+ * for other types of behavior.
+ * @return LayoutParams for use when adding the View to its parent.
+ */
+ public ViewGroup.MarginLayoutParams createLayoutParams() {
+ return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (!isAllowedToAutoHide()) setTranslationY(0.0f);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (!isAllowedToAutoHide()) setTranslationY(0.0f);
+ }
+
+ /**
+ * See {@link #android.view.ViewGroup.onLayout(boolean, int, int, int, int)}.
+ */
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Update the viewport height when the parent View's height changes (e.g. after rotation).
+ int currentParentHeight = getParent() == null ? 0 : ((View) getParent()).getHeight();
+ if (mParentHeight != currentParentHeight) {
+ mParentHeight = currentParentHeight;
+ mGestureState = Gesture.NONE;
+ if (mCurrentAnimation != null) mCurrentAnimation.end();
+ }
+
+ // Update the known effective height of the View.
+ MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
+ mTotalHeight = getMeasuredHeight() + params.topMargin + params.bottomMargin;
+
+ // Adding a listener to GestureListenerManager results in extra IPCs on every frame, which
+ // is very costly. Only attach the listener if needed.
+ if (mWebContents != null) {
+ if (mTotalHeight > 0) {
+ GestureListenerManager.fromWebContents(mWebContents)
+ .addListener(mGestureStateListener);
+ } else {
+ GestureListenerManager.fromWebContents(mWebContents)
+ .removeListener(mGestureStateListener);
+ }
+ }
+
+ super.onLayout(changed, l, t, r, b);
+ }
+
+ /**
+ * Creates a listener than monitors the WebContents for scrolls and flings.
+ * The listener updates the location of this View to account for the user's gestures.
+ * @return GestureStateListenerWithScroll to send to the WebContents.
+ */
+ private GestureStateListenerWithScroll createGestureStateListener() {
+ return new GestureStateListenerWithScroll() {
+ /** Tracks the previous event's scroll offset to determine if a scroll is up or down. */
+ private int mLastScrollOffsetY;
+
+ /** Location of the View when the current gesture was first started. */
+ private float mInitialTranslationY;
+
+ /** The initial extent of the scroll when triggered. */
+ private float mInitialExtentY;
+
+ @Override
+ public void onFlingStartGesture(int scrollOffsetY, int scrollExtentY) {
+ if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return;
+ resetInternalScrollState(scrollOffsetY, scrollExtentY);
+ mGestureState = Gesture.FLINGING;
+ }
+
+ @Override
+ public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
+ if (mGestureState != Gesture.FLINGING) return;
+ mGestureState = Gesture.NONE;
+
+ updateTranslation(scrollOffsetY, scrollExtentY);
+
+ boolean isScrollingDownward = scrollOffsetY > mLastScrollOffsetY;
+
+ boolean isVisibleInitially = mInitialTranslationY < mTotalHeight;
+ float percentageVisible = 1.0f - (getTranslationY() / mTotalHeight);
+ float visibilityThreshold = isVisibleInitially ? VERTICAL_FLING_HIDE_THRESHOLD
+ : VERTICAL_FLING_SHOW_THRESHOLD;
+ boolean isVisibleEnough = percentageVisible > visibilityThreshold;
+ boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_THRESHOLD);
+
+ boolean show = (!isScrollingDownward && isVisibleEnough) || isNearTopOfPage;
+
+ runUpEventAnimation(show);
+ }
+
+ @Override
+ public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
+ if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return;
+ resetInternalScrollState(scrollOffsetY, scrollExtentY);
+ mLastScrollOffsetY = scrollOffsetY;
+ mGestureState = Gesture.SCROLLING;
+ }
+
+ @Override
+ public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
+ if (mGestureState != Gesture.SCROLLING) return;
+ mGestureState = Gesture.NONE;
+
+ updateTranslation(scrollOffsetY, scrollExtentY);
+
+ runUpEventAnimation(shouldSnapToVisibleState(scrollOffsetY));
+ }
+
+ @Override
+ public void onScrollOffsetOrExtentChanged(int scrollOffsetY, int scrollExtentY) {
+ mLastScrollOffsetY = scrollOffsetY;
+
+ if (!shouldConsumeScroll(scrollOffsetY, scrollExtentY)) {
+ resetInternalScrollState(scrollOffsetY, scrollExtentY);
+ return;
+ }
+
+ // This function is called for both fling and scrolls.
+ if (mGestureState == Gesture.NONE || !cancelCurrentAnimation()
+ || isIndependentlyAnimating()) {
+ return;
+ }
+
+ updateTranslation(scrollOffsetY, scrollExtentY);
+ }
+
+ private void updateTranslation(int scrollOffsetY, int scrollExtentY) {
+ float scrollDiff =
+ (scrollOffsetY - mInitialOffsetY) + (scrollExtentY - mInitialExtentY);
+ float translation =
+ MathUtils.clamp(mInitialTranslationY + scrollDiff, mTotalHeight, 0);
+
+ // If the container has reached the completely shown position, reset the initial
+ // scroll so any movement will start hiding it again.
+ if (translation <= 0f) resetInternalScrollState(scrollOffsetY, scrollExtentY);
+
+ setTranslationY(translation);
+ }
+
+ /**
+ * Resets the internal values that a scroll or fling will base its calculations off of.
+ */
+ private void resetInternalScrollState(int scrollOffsetY, int scrollExtentY) {
+ mInitialOffsetY = scrollOffsetY;
+ mInitialExtentY = scrollExtentY;
+ mInitialTranslationY = getTranslationY();
+ }
+ };
+ }
+
+ /**
+ * @param scrollOffsetY The current scroll offset on the Y axis.
+ * @param scrollExtentY The current scroll extent on the Y axis.
+ * @return Whether or not the scroll should be consumed by the view.
+ */
+ protected boolean shouldConsumeScroll(int scrollOffsetY, int scrollExtentY) {
+ return true;
+ }
+
+ /**
+ * @param scrollOffsetY The current scroll offset on the Y axis.
+ * @return Whether the view should snap to a visible state.
+ */
+ protected boolean shouldSnapToVisibleState(int scrollOffsetY) {
+ boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_THRESHOLD);
+ boolean isVisibleEnough = getTranslationY() < mTotalHeight * FULL_THRESHOLD;
+ return isNearTopOfPage || isVisibleEnough;
+ }
+
+ /**
+ * @return Whether or not the view is animating independent of the user's scroll position.
+ */
+ protected boolean isIndependentlyAnimating() {
+ return false;
+ }
+
+ /**
+ * Creates a listener that is used only to animate the View coming onto the screen.
+ * @return The SimpleOnGestureListener that will monitor the View.
+ */
+ private View.OnLayoutChangeListener createLayoutChangeListener() {
+ return new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ removeOnLayoutChangeListener(mLayoutChangeListener);
+
+ // Animate the View coming in from the bottom of the screen.
+ setTranslationY(mTotalHeight);
+ mIsBeingDisplayedForFirstTime = true;
+ runUpEventAnimation(true);
+ }
+ };
+ }
+
+ /**
+ * Create an animation that snaps the View into position vertically.
+ * @param visible If true, snaps the View to the bottom-center of the screen. If false,
+ * translates the View below the bottom-center of the screen so that it is
+ * effectively invisible.
+ * @return An animator with the snap animation.
+ */
+ protected Animator createVerticalSnapAnimation(boolean visible) {
+ float targetTranslationY = visible ? 0.0f : mTotalHeight;
+ float yDifference = Math.abs(targetTranslationY - getTranslationY()) / mTotalHeight;
+ long duration = Math.max(0, (long) (ANIMATION_DURATION_MS * yDifference));
+
+ Animator animator = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, targetTranslationY);
+ animator.setDuration(duration);
+ animator.setInterpolator(mInterpolator);
+
+ return animator;
+ }
+
+ /**
+ * Run an animation when a gesture has ended (an 'up' motion event).
+ * @param visible Whether or not the view should be visible.
+ */
+ protected void runUpEventAnimation(boolean visible) {
+ if (mCurrentAnimation != null) mCurrentAnimation.cancel();
+ mCurrentAnimation = createVerticalSnapAnimation(visible);
+ mCurrentAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mGestureState = Gesture.NONE;
+ mCurrentAnimation = null;
+ mIsBeingDisplayedForFirstTime = false;
+ }
+ });
+ mCurrentAnimation.start();
+ }
+
+ /**
+ * Cancels the current animation, unless the View is coming onto the screen for the first time.
+ * @return True if the animation was canceled or wasn't running, false otherwise.
+ */
+ private boolean cancelCurrentAnimation() {
+ if (mIsBeingDisplayedForFirstTime) return false;
+ if (mCurrentAnimation != null) mCurrentAnimation.cancel();
+ return true;
+ }
+
+ /**
+ * @return Whether the SwipableOverlayView is allowed to hide itself on scroll.
+ */
+ protected boolean isAllowedToAutoHide() {
+ return true;
+ }
+
+ /**
+ * Override gatherTransparentRegion to make this view's layout a placeholder for its
+ * animations. This is only called during layout, so it doesn't really make sense to apply
+ * post-layout properties like it does by default. Together with setWillNotDraw(false),
+ * this ensures no child animation within this view's layout will be clipped by a SurfaceView.
+ */
+ @Override
+ public boolean gatherTransparentRegion(Region region) {
+ float translationY = getTranslationY();
+ setTranslationY(0);
+ boolean result = super.gatherTransparentRegion(region);
+ // Restoring TranslationY invalidates this view unnecessarily. However, this function
+ // is called as part of layout, which implies a full redraw is about to occur anyway.
+ setTranslationY(translationY);
+ return result;
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
index 38d6cf9557c..382bfb7c605 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -18,13 +18,16 @@ import android.view.ViewStructure;
import android.view.autofill.AutofillValue;
import android.webkit.ValueCallback;
+import androidx.annotation.VisibleForTesting;
+
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.components.autofill.AutofillActionModeCallback;
import org.chromium.components.autofill.AutofillProvider;
-import org.chromium.components.autofill.AutofillProviderImpl;
+import org.chromium.components.browser_ui.http_auth.LoginPrompt;
+import org.chromium.components.browser_ui.media.MediaSessionHelper;
import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
@@ -55,7 +58,9 @@ 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.IWebMessageCallbackClient;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ScrollNotificationType;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.util.ArrayList;
@@ -67,7 +72,7 @@ import java.util.Map;
* Implementation of ITab.
*/
@JNINamespace("weblayer")
-public final class TabImpl extends ITab.Stub {
+public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer {
private static int sNextId = 1;
// Map from id to TabImpl.
private static final Map<Integer, TabImpl> sTabMap = new HashMap<Integer, TabImpl>();
@@ -83,6 +88,7 @@ public final class TabImpl extends ITab.Stub {
private TabViewAndroidDelegate mViewAndroidDelegate;
// BrowserImpl this TabImpl is in. This is only null during creation.
private BrowserImpl mBrowser;
+ private LoginPrompt mLoginPrompt;
/**
* The AutofillProvider that integrates with system-level autofill. This is null until
* updateFromBrowser() is invoked.
@@ -107,10 +113,12 @@ public final class TabImpl extends ITab.Stub {
private boolean mWaitingForMatchRects;
private InterceptNavigationDelegateClientImpl mInterceptNavigationDelegateClient;
private InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
+ private InfoBarContainer mInfoBarContainer;
+ private MediaSessionHelper mMediaSessionHelper;
private boolean mPostContainerViewInitDone;
- private AccessibilityUtil.Observer mAccessibilityObserver;
+ private WebLayerAccessibilityUtil.Observer mAccessibilityObserver;
private static class InternalAccessDelegateImpl
implements ViewEventSink.InternalAccessDelegate {
@@ -165,6 +173,37 @@ public final class TabImpl extends ITab.Stub {
viewController.onBottomControlsChanged(bottomControlsOffsetY);
}
}
+
+ @Override
+ public void onBackgroundColorChanged(int color) {
+ if (WebLayerFactoryImpl.getClientMajorVersion() >= 85) {
+ try {
+ mClient.onBackgroundColorChanged(color);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+ }
+
+ @Override
+ protected void onVerticalScrollDirectionChanged(
+ boolean directionUp, float currentScrollRatio) {
+ if (WebLayerFactoryImpl.getClientMajorVersion() >= 85) {
+ try {
+ mClient.onScrollNotification(directionUp
+ ? ScrollNotificationType.DIRECTION_CHANGED_UP
+ : ScrollNotificationType.DIRECTION_CHANGED_DOWN,
+ currentScrollRatio);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+ }
+ }
+
+ public static TabImpl fromWebContents(WebContents webContents) {
+ if (webContents == null || webContents.isDestroyed()) return null;
+ return TabImplJni.get().fromWebContents(webContents);
}
public static TabImpl getTabById(int tabId) {
@@ -223,12 +262,21 @@ public final class TabImpl extends ITab.Stub {
mInterceptNavigationDelegateClient.initializeWithDelegate(mInterceptNavigationDelegate);
sTabMap.put(mId, this);
+ mInfoBarContainer = new InfoBarContainer(this);
mAccessibilityObserver = (boolean enabled) -> {
setBrowserControlsVisibilityConstraint(ImplControlsVisibilityReason.ACCESSIBILITY,
enabled ? BrowserControlsState.SHOWN : BrowserControlsState.BOTH);
};
// addObserver() calls to observer when added.
WebLayerAccessibilityUtil.get().addObserver(mAccessibilityObserver);
+
+ // MediaSession only works if the client is new enough. Sadly, passing
+ // kDisableMediaSessionAPI does not fully disable the API, so a check is also necessary
+ // before installing this observer.
+ if (WebLayerFactoryImpl.getClientMajorVersion() >= 85) {
+ mMediaSessionHelper = new MediaSessionHelper(
+ mWebContents, MediaSessionManager.createMediaSessionHelperDelegate(mId));
+ }
}
private void doInitAfterSettingContainerView() {
@@ -280,7 +328,7 @@ public final class TabImpl extends ITab.Stub {
// Set up |mAutofillProvider| to operate in the new Context. It's safe to assume
// the context won't change unless it is first nulled out, since the fragment
// must be detached before it can be reattached to a new Context.
- mAutofillProvider = new AutofillProviderImpl(
+ mAutofillProvider = new AutofillProvider(
mBrowser.getContext(), mBrowser.getAutofillView(), "WebLayer");
TabImplJni.get().onAutofillProviderChanged(mNativeTab, mAutofillProvider);
}
@@ -333,17 +381,27 @@ public final class TabImpl extends ITab.Stub {
assert mBrowser != null;
TabImplJni.get().setBrowserControlsContainerViews(
mNativeTab, topControlsContainerViewHandle, bottomControlsContainerViewHandle);
+ mInfoBarContainer.onTabDidGainActive();
updateWebContentsVisibility();
- mWebContents.onShow();
}
/**
* Called when this TabImpl is no longer the active TabImpl.
*/
public void onDidLoseActive() {
+ if (mAutofillProvider != null) {
+ mAutofillProvider.hidePopup();
+ }
+
hideFindInPageUiAndNotifyClient();
- mWebContents.onHide();
updateWebContentsVisibility();
+
+ // This method is called as part of the final phase of TabImpl destruction, at which
+ // point mInfoBarContainer has already been destroyed.
+ if (mInfoBarContainer != null) {
+ mInfoBarContainer.onTabDidLoseActive();
+ }
+
TabImplJni.get().setBrowserControlsContainerViews(mNativeTab, 0, 0);
}
@@ -351,7 +409,8 @@ public final class TabImpl extends ITab.Stub {
* Returns whether this Tab is visible.
*/
public boolean isVisible() {
- return (mBrowser.getActiveTab() == this && mBrowser.isStarted());
+ return (mBrowser.getActiveTab() == this
+ && (mBrowser.isStarted() || mBrowser.isFragmentStoppedForConfigurationChange()));
}
private void updateWebContentsVisibility() {
@@ -379,10 +438,17 @@ public final class TabImpl extends ITab.Stub {
return mWebContents;
}
- long getNativeTab() {
+ // Public for tests.
+ @VisibleForTesting
+ public long getNativeTab() {
return mNativeTab;
}
+ @VisibleForTesting
+ public InfoBarContainer getInfoBarContainerForTesting() {
+ return mInfoBarContainer;
+ }
+
@Override
public NavigationControllerImpl createNavigationController(INavigationControllerClient client) {
StrictModeWorkaround.apply();
@@ -522,6 +588,7 @@ public final class TabImpl extends ITab.Stub {
@Override
public boolean dismissTransientUi() {
+ StrictModeWorkaround.apply();
BrowserViewController viewController = getViewController();
if (viewController != null && viewController.dismissTabModalOverlay()) return true;
@@ -541,10 +608,34 @@ public final class TabImpl extends ITab.Stub {
@Override
public String getGuid() {
+ StrictModeWorkaround.apply();
return TabImplJni.get().getGuid(mNativeTab);
}
@Override
+ public boolean setData(Map data) {
+ StrictModeWorkaround.apply();
+ String[] flattenedMap = new String[data.size() * 2];
+ int i = 0;
+ for (Map.Entry<String, String> entry : ((Map<String, String>) data).entrySet()) {
+ flattenedMap[i++] = entry.getKey();
+ flattenedMap[i++] = entry.getValue();
+ }
+ return TabImplJni.get().setData(mNativeTab, flattenedMap);
+ }
+
+ @Override
+ public Map getData() {
+ StrictModeWorkaround.apply();
+ String[] data = TabImplJni.get().getData(mNativeTab);
+ Map<String, String> map = new HashMap<>();
+ for (int i = 0; i < data.length; i += 2) {
+ map.put(data[i], data[i + 1]);
+ }
+ return map;
+ }
+
+ @Override
public void captureScreenShot(float scale, IObjectWrapper valueCallback) {
StrictModeWorkaround.apply();
ValueCallback<Pair<Bitmap, Integer>> unwrappedCallback =
@@ -553,6 +644,18 @@ public final class TabImpl extends ITab.Stub {
TabImplJni.get().captureScreenShot(mNativeTab, scale, unwrappedCallback);
}
+ @Override
+ public boolean canTranslate() {
+ StrictModeWorkaround.apply();
+ return TabImplJni.get().canTranslate(mNativeTab);
+ }
+
+ @Override
+ public void showTranslateUi() {
+ StrictModeWorkaround.apply();
+ TabImplJni.get().showTranslateUi(mNativeTab);
+ }
+
@CalledByNative
private static void runCaptureScreenShotCallback(
ValueCallback<Pair<Bitmap, Integer>> callback, Bitmap bitmap, int errorCode) {
@@ -634,6 +737,53 @@ public final class TabImpl extends ITab.Stub {
getBrowser().destroyTab(this);
}
+ @CalledByNative
+ private void showHttpAuthPrompt(String host, String url) {
+ mLoginPrompt = new LoginPrompt(mBrowser.getContext(), host, url, this);
+ mLoginPrompt.show();
+ }
+
+ @CalledByNative
+ private void closeHttpAuthPrompt() {
+ mLoginPrompt = null;
+ }
+
+ @Override
+ public void cancel() {
+ TabImplJni.get().cancelHttpAuth(mNativeTab);
+ }
+
+ @Override
+ public void proceed(String username, String password) {
+ TabImplJni.get().setHttpAuth(mNativeTab, username, password);
+ }
+
+ @Override
+ public void registerWebMessageCallback(
+ String jsObjectName, List<String> allowedOrigins, IWebMessageCallbackClient client) {
+ if (jsObjectName.isEmpty()) {
+ throw new IllegalArgumentException("JS object name must not be empty");
+ }
+ if (allowedOrigins.isEmpty()) {
+ throw new IllegalArgumentException("At least one origin must be specified");
+ }
+ for (String origin : allowedOrigins) {
+ if (TextUtils.isEmpty(origin)) {
+ throw new IllegalArgumentException("Origin must not be non-empty");
+ }
+ }
+ String registerError = TabImplJni.get().registerWebMessageCallback(mNativeTab, jsObjectName,
+ allowedOrigins.toArray(new String[allowedOrigins.size()]), client);
+ if (!TextUtils.isEmpty(registerError)) {
+ throw new IllegalArgumentException(registerError);
+ }
+ }
+
+ @Override
+ public void unregisterWebMessageCallback(String jsObjectName) {
+ TabImplJni.get().unregisterWebMessageCallback(mNativeTab, jsObjectName);
+ }
+
public void destroy() {
// Ensure that this method isn't called twice.
assert mInterceptNavigationDelegate != null;
@@ -668,6 +818,9 @@ public final class TabImpl extends ITab.Stub {
mInterceptNavigationDelegateClient.destroy();
mInterceptNavigationDelegate = null;
+ mInfoBarContainer.destroy();
+ mInfoBarContainer = null;
+
mMediaStreamManager.destroy();
mMediaStreamManager = null;
@@ -734,6 +887,11 @@ public final class TabImpl extends ITab.Stub {
onBrowserControlsStateUpdated(mBrowserControlsVisibility.get());
}
+ @VisibleForTesting
+ public boolean canBrowserControlsScrollForTesting() {
+ return mBrowserControlsVisibility.get() == BrowserControlsState.BOTH;
+ }
+
private void onBrowserControlsStateUpdated(int state) {
// If something has overridden the FIP's SHOWN constraint, cancel FIP. This causes FIP to
// dismiss when entering fullscreen.
@@ -770,8 +928,14 @@ public final class TabImpl extends ITab.Stub {
return (mBrowser.getActiveTab() == this) ? mBrowser.getViewController() : null;
}
+ @VisibleForTesting
+ public boolean canInfoBarContainerScrollForTesting() {
+ return mInfoBarContainer.getContainerViewForTesting().isAllowedToAutoHide();
+ }
+
@NativeMethods
interface Natives {
+ TabImpl fromWebContents(WebContents webContents);
long createTab(long profile, TabImpl caller);
void setJavaImpl(long nativeTabImpl, TabImpl impl);
void onAutofillProviderChanged(long nativeTabImpl, AutofillProvider autofillProvider);
@@ -786,6 +950,15 @@ public final class TabImpl extends ITab.Stub {
String getGuid(long nativeTabImpl);
void captureScreenShot(long nativeTabImpl, float scale,
ValueCallback<Pair<Bitmap, Integer>> valueCallback);
+ boolean setData(long nativeTabImpl, String[] data);
+ String[] getData(long nativeTabImpl);
boolean isRendererControllingBrowserControlsOffsets(long nativeTabImpl);
+ void setHttpAuth(long nativeTabImpl, String username, String password);
+ void cancelHttpAuth(long nativeTabImpl);
+ String registerWebMessageCallback(long nativeTabImpl, String jsObjectName,
+ String[] allowedOrigins, IWebMessageCallbackClient client);
+ void unregisterWebMessageCallback(long nativeTabImpl, String jsObjectName);
+ boolean canTranslate(long nativeTabImpl);
+ void showTranslateUi(long nativeTabImpl);
}
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java
new file mode 100644
index 00000000000..a97315e6fa4
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java
@@ -0,0 +1,578 @@
+// Copyright 2017 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.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.chromium.base.StrictModeContext;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.ui.widget.Toast;
+
+/**
+ * Java version of the compact translate infobar.
+ */
+@JNINamespace("weblayer")
+public class TranslateCompactInfoBar extends InfoBar
+ implements TabLayout.OnTabSelectedListener, TranslateMenuHelper.TranslateMenuListener {
+ public static final int TRANSLATING_INFOBAR = 1;
+ public static final int AFTER_TRANSLATING_INFOBAR = 2;
+
+ private static final int SOURCE_TAB_INDEX = 0;
+ private static final int TARGET_TAB_INDEX = 1;
+
+ // Action ID for Snackbar.
+ // Actions performed by clicking on on the overflow menu.
+ public static final int ACTION_OVERFLOW_ALWAYS_TRANSLATE = 0;
+ public static final int ACTION_OVERFLOW_NEVER_SITE = 1;
+ public static final int ACTION_OVERFLOW_NEVER_LANGUAGE = 2;
+ // Actions triggered automatically. (when translation or denied count reaches the threshold.)
+ public static final int ACTION_AUTO_ALWAYS_TRANSLATE = 3;
+ public static final int ACTION_AUTO_NEVER_LANGUAGE = 4;
+
+ private final int mInitialStep;
+ private final int mDefaultTextColor;
+ private final TranslateOptions mOptions;
+
+ private long mNativeTranslateInfoBarPtr;
+ private TranslateTabLayout mTabLayout;
+
+ // Metric to track the total number of translations in a page, including reverts to original.
+ private int mTotalTranslationCount;
+
+ // Histogram names for logging metrics.
+ private static final String INFOBAR_HISTOGRAM_TRANSLATE_LANGUAGE =
+ "Translate.CompactInfobar.Language.Translate";
+ private static final String INFOBAR_HISTOGRAM_MORE_LANGUAGES_LANGUAGE =
+ "Translate.CompactInfobar.Language.MoreLanguages";
+ private static final String INFOBAR_HISTOGRAM_PAGE_NOT_IN_LANGUAGE =
+ "Translate.CompactInfobar.Language.PageNotIn";
+ private static final String INFOBAR_HISTOGRAM_ALWAYS_TRANSLATE_LANGUAGE =
+ "Translate.CompactInfobar.Language.AlwaysTranslate";
+ private static final String INFOBAR_HISTOGRAM_NEVER_TRANSLATE_LANGUAGE =
+ "Translate.CompactInfobar.Language.NeverTranslate";
+ private static final String INFOBAR_HISTOGRAM = "Translate.CompactInfobar.Event";
+ private static final String INFOBAR_HISTOGRAM_TRANSLATION_COUNT =
+ "Translate.CompactInfobar.TranslationsPerPage";
+
+ /**
+ * This is used to back a UMA histogram, so it should be treated as
+ * append-only. The values should not be changed or reused, and
+ * INFOBAR_HISTOGRAM_BOUNDARY should be the last.
+ */
+ private static final int INFOBAR_IMPRESSION = 0;
+ private static final int INFOBAR_TARGET_TAB_TRANSLATE = 1;
+ private static final int INFOBAR_DECLINE = 2;
+ private static final int INFOBAR_OPTIONS = 3;
+ private static final int INFOBAR_MORE_LANGUAGES = 4;
+ private static final int INFOBAR_MORE_LANGUAGES_TRANSLATE = 5;
+ private static final int INFOBAR_PAGE_NOT_IN = 6;
+ private static final int INFOBAR_ALWAYS_TRANSLATE = 7;
+ private static final int INFOBAR_NEVER_TRANSLATE = 8;
+ private static final int INFOBAR_NEVER_TRANSLATE_SITE = 9;
+ private static final int INFOBAR_SCROLL_HIDE = 10;
+ private static final int INFOBAR_SCROLL_SHOW = 11;
+ private static final int INFOBAR_REVERT = 12;
+ private static final int INFOBAR_SNACKBAR_ALWAYS_TRANSLATE_IMPRESSION = 13;
+ private static final int INFOBAR_SNACKBAR_NEVER_TRANSLATE_IMPRESSION = 14;
+ private static final int INFOBAR_SNACKBAR_NEVER_TRANSLATE_SITE_IMPRESSION = 15;
+ private static final int INFOBAR_SNACKBAR_CANCEL_ALWAYS = 16;
+ private static final int INFOBAR_SNACKBAR_CANCEL_NEVER_SITE = 17;
+ private static final int INFOBAR_SNACKBAR_CANCEL_NEVER = 18;
+ private static final int INFOBAR_ALWAYS_TRANSLATE_UNDO = 19;
+ private static final int INFOBAR_CLOSE_DEPRECATED = 20;
+ private static final int INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION = 21;
+ private static final int INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION = 22;
+ private static final int INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS = 23;
+ private static final int INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER = 24;
+ private static final int INFOBAR_HISTOGRAM_BOUNDARY = 25;
+
+ // Need 2 instances of TranslateMenuHelper to prevent a race condition bug which happens when
+ // showing language menu after dismissing overflow menu.
+ private TranslateMenuHelper mOverflowMenuHelper;
+ private TranslateMenuHelper mLanguageMenuHelper;
+
+ private ImageButton mMenuButton;
+ private InfoBarCompactLayout mParent;
+
+ private boolean mMenuExpanded;
+ private boolean mIsFirstLayout = true;
+ private boolean mUserInteracted;
+
+ @CalledByNative
+ private static InfoBar create(TabImpl tab, int initialStep, String sourceLanguageCode,
+ String targetLanguageCode, boolean alwaysTranslate, boolean triggeredFromMenu,
+ String[] languages, String[] languageCodes, int[] hashCodes, int tabTextColor) {
+ recordInfobarAction(INFOBAR_IMPRESSION);
+ return new TranslateCompactInfoBar(initialStep, sourceLanguageCode, targetLanguageCode,
+ alwaysTranslate, triggeredFromMenu, languages, languageCodes, hashCodes,
+ tabTextColor);
+ }
+
+ TranslateCompactInfoBar(int initialStep, String sourceLanguageCode, String targetLanguageCode,
+ boolean alwaysTranslate, boolean triggeredFromMenu, String[] languages,
+ String[] languageCodes, int[] hashCodes, int tabTextColor) {
+ super(R.drawable.infobar_translate_compact, 0, null, null);
+
+ mInitialStep = initialStep;
+ mDefaultTextColor = tabTextColor;
+ mOptions = TranslateOptions.create(sourceLanguageCode, targetLanguageCode, languages,
+ languageCodes, alwaysTranslate, triggeredFromMenu, hashCodes);
+ }
+
+ @Override
+ protected boolean usesCompactLayout() {
+ return true;
+ }
+
+ @Override
+ protected void createCompactLayoutContent(InfoBarCompactLayout parent) {
+ LinearLayout content;
+ // LayoutInflater may trigger accessing the disk.
+ try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+ content = (LinearLayout) LayoutInflater.from(getContext())
+ .inflate(R.layout.weblayer_infobar_translate_compact_content, parent,
+ false);
+ }
+
+ // When parent tab is being switched out (view detached), dismiss all menus and snackbars.
+ content.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {}
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ dismissMenusAndSnackbars();
+ }
+ });
+
+ mTabLayout =
+ (TranslateTabLayout) content.findViewById(R.id.weblayer_translate_infobar_tabs);
+ if (mDefaultTextColor > 0) {
+ mTabLayout.setTabTextColors(
+ ContextCompat.getColor(getContext(), R.color.default_text_color),
+ ContextCompat.getColor(
+ getContext(), R.color.weblayer_tab_layout_selected_tab_color));
+ }
+ mTabLayout.addTabs(mOptions.sourceLanguageName(), mOptions.targetLanguageName());
+
+ if (mInitialStep == TRANSLATING_INFOBAR) {
+ // Set translating status in the beginning for pages translated automatically.
+ mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
+ mTabLayout.showProgressBarOnTab(TARGET_TAB_INDEX);
+ mUserInteracted = true;
+ } else if (mInitialStep == AFTER_TRANSLATING_INFOBAR) {
+ // Focus on target tab since we are after translation.
+ mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
+ }
+
+ mTabLayout.addOnTabSelectedListener(this);
+
+ // Dismiss all menus and end scrolling animation when there is layout changed.
+ mTabLayout.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
+ // Dismiss all menus to prevent menu misplacement.
+ dismissMenus();
+
+ if (mIsFirstLayout) {
+ // Scrolls to the end to make sure the target language tab is visible when
+ // language tabs is too long.
+ mTabLayout.startScrollingAnimationIfNeeded();
+ mIsFirstLayout = false;
+ return;
+ }
+
+ // End scrolling animation when layout changed.
+ mTabLayout.endScrollingAnimationIfPlaying();
+ }
+ }
+ });
+
+ mMenuButton = content.findViewById(R.id.weblayer_translate_infobar_menu_button);
+ mMenuButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mTabLayout.endScrollingAnimationIfPlaying();
+ recordInfobarAction(INFOBAR_OPTIONS);
+ initMenuHelper(TranslateMenu.MENU_OVERFLOW);
+ mOverflowMenuHelper.show(TranslateMenu.MENU_OVERFLOW, getParentWidth());
+ mMenuExpanded = true;
+ }
+ });
+
+ parent.addContent(content, 1.0f);
+ mParent = parent;
+ }
+
+ private void initMenuHelper(int menuType) {
+ boolean isIncognito = TranslateCompactInfoBarJni.get().isIncognito(
+ mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this);
+ switch (menuType) {
+ case TranslateMenu.MENU_OVERFLOW:
+ if (mOverflowMenuHelper == null) {
+ mOverflowMenuHelper = new TranslateMenuHelper(
+ getContext(), mMenuButton, mOptions, this, isIncognito);
+ }
+ return;
+ case TranslateMenu.MENU_TARGET_LANGUAGE:
+ case TranslateMenu.MENU_SOURCE_LANGUAGE:
+ if (mLanguageMenuHelper == null) {
+ mLanguageMenuHelper = new TranslateMenuHelper(
+ getContext(), mMenuButton, mOptions, this, isIncognito);
+ }
+ return;
+ default:
+ assert false : "Unsupported Menu Item Id";
+ }
+ }
+
+ private void startTranslating(int tabPosition) {
+ if (TARGET_TAB_INDEX == tabPosition) {
+ // Already on the target tab.
+ mTabLayout.showProgressBarOnTab(TARGET_TAB_INDEX);
+ onButtonClicked(ActionType.TRANSLATE);
+ mUserInteracted = true;
+ } else {
+ mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
+ }
+ }
+
+ @CalledByNative
+ private void onPageTranslated(int errorType) {
+ incrementAndRecordTranslationsPerPageCount();
+ if (mTabLayout != null) {
+ mTabLayout.hideProgressBar();
+ if (errorType != 0) {
+ Toast.makeText(getContext(), R.string.translate_infobar_error, Toast.LENGTH_SHORT)
+ .show();
+ // Disable OnTabSelectedListener then revert selection.
+ mTabLayout.removeOnTabSelectedListener(this);
+ mTabLayout.getTabAt(SOURCE_TAB_INDEX).select();
+ // Add OnTabSelectedListener back.
+ mTabLayout.addOnTabSelectedListener(this);
+ }
+ }
+ }
+
+ @CalledByNative
+ private void setNativePtr(long nativePtr) {
+ mNativeTranslateInfoBarPtr = nativePtr;
+ }
+
+ @CalledByNative
+ private void setAutoAlwaysTranslate() {
+ createAndShowSnackbar(ACTION_AUTO_ALWAYS_TRANSLATE);
+ }
+
+ @Override
+ protected void onNativeDestroyed() {
+ mNativeTranslateInfoBarPtr = 0;
+ super.onNativeDestroyed();
+ }
+
+ private void closeInfobar(boolean explicitly) {
+ if (isDismissed()) return;
+
+ if (!mUserInteracted) {
+ recordInfobarAction(INFOBAR_DECLINE);
+ }
+
+ // NOTE: In Chrome there is a check for whether auto "never translate" should be triggered
+ // via a snackbar here. However, WebLayer does not have snackbars and thus does not have
+ // this check as there would be no way to inform the user of the functionality being
+ // triggered. The user of course has the option of choosing "never translate" from the
+ // overflow menu.
+
+ // This line will dismiss this infobar.
+ super.onCloseButtonClicked();
+ }
+
+ @Override
+ public void onCloseButtonClicked() {
+ mTabLayout.endScrollingAnimationIfPlaying();
+ closeInfobar(true);
+ }
+
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ switch (tab.getPosition()) {
+ case SOURCE_TAB_INDEX:
+ incrementAndRecordTranslationsPerPageCount();
+ recordInfobarAction(INFOBAR_REVERT);
+ onButtonClicked(ActionType.TRANSLATE_SHOW_ORIGINAL);
+ return;
+ case TARGET_TAB_INDEX:
+ recordInfobarAction(INFOBAR_TARGET_TAB_TRANSLATE);
+ recordInfobarLanguageData(
+ INFOBAR_HISTOGRAM_TRANSLATE_LANGUAGE, mOptions.targetLanguageCode());
+ startTranslating(TARGET_TAB_INDEX);
+ return;
+ default:
+ assert false : "Unexpected Tab Index";
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {}
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {}
+
+ @Override
+ public void onOverflowMenuItemClicked(int itemId) {
+ switch (itemId) {
+ case TranslateMenu.ID_OVERFLOW_MORE_LANGUAGE:
+ recordInfobarAction(INFOBAR_MORE_LANGUAGES);
+ initMenuHelper(TranslateMenu.MENU_TARGET_LANGUAGE);
+ mLanguageMenuHelper.show(TranslateMenu.MENU_TARGET_LANGUAGE, getParentWidth());
+ return;
+ case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE:
+ // Only show snackbar when "Always Translate" is enabled.
+ if (!mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)) {
+ recordInfobarAction(INFOBAR_ALWAYS_TRANSLATE);
+ recordInfobarLanguageData(INFOBAR_HISTOGRAM_ALWAYS_TRANSLATE_LANGUAGE,
+ mOptions.sourceLanguageCode());
+ createAndShowSnackbar(ACTION_OVERFLOW_ALWAYS_TRANSLATE);
+ } else {
+ recordInfobarAction(INFOBAR_ALWAYS_TRANSLATE_UNDO);
+ handleTranslateOptionPostSnackbar(ACTION_OVERFLOW_ALWAYS_TRANSLATE);
+ }
+ return;
+ case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE:
+ recordInfobarAction(INFOBAR_NEVER_TRANSLATE);
+ recordInfobarLanguageData(
+ INFOBAR_HISTOGRAM_NEVER_TRANSLATE_LANGUAGE, mOptions.sourceLanguageCode());
+ createAndShowSnackbar(ACTION_OVERFLOW_NEVER_LANGUAGE);
+ return;
+ case TranslateMenu.ID_OVERFLOW_NEVER_SITE:
+ recordInfobarAction(INFOBAR_NEVER_TRANSLATE_SITE);
+ createAndShowSnackbar(ACTION_OVERFLOW_NEVER_SITE);
+ return;
+ case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE:
+ recordInfobarAction(INFOBAR_PAGE_NOT_IN);
+ initMenuHelper(TranslateMenu.MENU_SOURCE_LANGUAGE);
+ mLanguageMenuHelper.show(TranslateMenu.MENU_SOURCE_LANGUAGE, getParentWidth());
+ return;
+ default:
+ assert false : "Unexpected overflow menu code";
+ }
+ }
+
+ @Override
+ public void onTargetMenuItemClicked(String code) {
+ // Reset target code in both UI and native.
+ if (mNativeTranslateInfoBarPtr != 0 && mOptions.setTargetLanguage(code)) {
+ recordInfobarAction(INFOBAR_MORE_LANGUAGES_TRANSLATE);
+ recordInfobarLanguageData(
+ INFOBAR_HISTOGRAM_MORE_LANGUAGES_LANGUAGE, mOptions.targetLanguageCode());
+ TranslateCompactInfoBarJni.get().applyStringTranslateOption(mNativeTranslateInfoBarPtr,
+ TranslateCompactInfoBar.this, TranslateOption.TARGET_CODE, code);
+ // Adjust UI.
+ mTabLayout.replaceTabTitle(TARGET_TAB_INDEX, mOptions.getRepresentationFromCode(code));
+ startTranslating(mTabLayout.getSelectedTabPosition());
+ }
+ }
+
+ @Override
+ public void onSourceMenuItemClicked(String code) {
+ // Reset source code in both UI and native.
+ if (mNativeTranslateInfoBarPtr != 0 && mOptions.setSourceLanguage(code)) {
+ recordInfobarLanguageData(
+ INFOBAR_HISTOGRAM_PAGE_NOT_IN_LANGUAGE, mOptions.sourceLanguageCode());
+ TranslateCompactInfoBarJni.get().applyStringTranslateOption(mNativeTranslateInfoBarPtr,
+ TranslateCompactInfoBar.this, TranslateOption.SOURCE_CODE, code);
+ // Adjust UI.
+ mTabLayout.replaceTabTitle(SOURCE_TAB_INDEX, mOptions.getRepresentationFromCode(code));
+ startTranslating(mTabLayout.getSelectedTabPosition());
+ }
+ }
+
+ // Dismiss all overflow menus that remains open.
+ // This is called when infobar started hiding or layout changed.
+ private void dismissMenus() {
+ if (mOverflowMenuHelper != null) mOverflowMenuHelper.dismiss();
+ if (mLanguageMenuHelper != null) mLanguageMenuHelper.dismiss();
+ }
+
+ // Dismiss all overflow menus and snackbars that belong to this infobar and remain open.
+ private void dismissMenusAndSnackbars() {
+ dismissMenus();
+ }
+
+ @Override
+ protected void onStartedHiding() {
+ dismissMenusAndSnackbars();
+ }
+
+ @Override
+ protected CharSequence getAccessibilityMessage(CharSequence defaultMessage) {
+ return getContext().getString(R.string.translate_button);
+ }
+
+ /**
+ * Returns true if overflow menu is showing. This is only used for automation testing.
+ */
+ public boolean isShowingOverflowMenuForTesting() {
+ if (mOverflowMenuHelper == null) return false;
+ return mOverflowMenuHelper.isShowing();
+ }
+
+ /**
+ * Returns true if language menu is showing. This is only used for automation testing.
+ */
+ public boolean isShowingLanguageMenuForTesting() {
+ if (mLanguageMenuHelper == null) return false;
+ return mLanguageMenuHelper.isShowing();
+ }
+
+ /**
+ * Returns true if the tab at the given |tabIndex| is selected. This is only used for automation
+ * testing.
+ */
+ private boolean isTabSelectedForTesting(int tabIndex) {
+ return mTabLayout.getTabAt(tabIndex).isSelected();
+ }
+
+ /**
+ * Returns true if the target tab is selected. This is only used for automation testing.
+ */
+ public boolean isSourceTabSelectedForTesting() {
+ return this.isTabSelectedForTesting(SOURCE_TAB_INDEX);
+ }
+
+ /**
+ * Returns true if the target tab is selected. This is only used for automation testing.
+ */
+ public boolean isTargetTabSelectedForTesting() {
+ return this.isTabSelectedForTesting(TARGET_TAB_INDEX);
+ }
+
+ private void createAndShowSnackbar(int actionId) {
+ // NOTE: WebLayer doesn't have snackbars, so the relevant action is just taken directly.
+ // TODO(blundell): If WebLayer ends up staying with this implementation long-term, update
+ // the nomenclature of this file to avoid any references to snackbars.
+ handleTranslateOptionPostSnackbar(actionId);
+ }
+
+ private void handleTranslateOptionPostSnackbar(int actionId) {
+ // Quit if native is destroyed.
+ if (mNativeTranslateInfoBarPtr == 0) return;
+
+ switch (actionId) {
+ case ACTION_OVERFLOW_ALWAYS_TRANSLATE:
+ toggleAlwaysTranslate();
+ // Start translating if always translate is selected and if page is not already
+ // translated to the target language.
+ if (mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)
+ && mTabLayout.getSelectedTabPosition() == SOURCE_TAB_INDEX) {
+ startTranslating(mTabLayout.getSelectedTabPosition());
+ }
+ return;
+ case ACTION_AUTO_ALWAYS_TRANSLATE:
+ toggleAlwaysTranslate();
+ return;
+ case ACTION_OVERFLOW_NEVER_LANGUAGE:
+ case ACTION_AUTO_NEVER_LANGUAGE:
+ mUserInteracted = true;
+ mOptions.toggleNeverTranslateLanguageState(
+ !mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE));
+ TranslateCompactInfoBarJni.get().applyBoolTranslateOption(
+ mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this,
+ TranslateOption.NEVER_TRANSLATE,
+ mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE));
+ return;
+ case ACTION_OVERFLOW_NEVER_SITE:
+ mUserInteracted = true;
+ mOptions.toggleNeverTranslateDomainState(
+ !mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN));
+ TranslateCompactInfoBarJni.get().applyBoolTranslateOption(
+ mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this,
+ TranslateOption.NEVER_TRANSLATE_SITE,
+ mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN));
+ return;
+ default:
+ assert false : "Unsupported Menu Item Id, in handle post snackbar";
+ }
+ }
+
+ private void toggleAlwaysTranslate() {
+ mOptions.toggleAlwaysTranslateLanguageState(
+ !mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE));
+ TranslateCompactInfoBarJni.get().applyBoolTranslateOption(mNativeTranslateInfoBarPtr,
+ TranslateCompactInfoBar.this, TranslateOption.ALWAYS_TRANSLATE,
+ mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE));
+ }
+
+ private static void recordInfobarAction(int action) {
+ RecordHistogram.recordEnumeratedHistogram(
+ INFOBAR_HISTOGRAM, action, INFOBAR_HISTOGRAM_BOUNDARY);
+ }
+
+ private void recordInfobarLanguageData(String histogram, String langCode) {
+ Integer hashCode = mOptions.getUMAHashCodeFromCode(langCode);
+ if (hashCode != null) {
+ RecordHistogram.recordSparseHistogram(histogram, hashCode);
+ }
+ }
+
+ private void incrementAndRecordTranslationsPerPageCount() {
+ RecordHistogram.recordCountHistogram(
+ INFOBAR_HISTOGRAM_TRANSLATION_COUNT, ++mTotalTranslationCount);
+ }
+
+ // Return the width of parent in pixels. Return 0 if there is no parent.
+ private int getParentWidth() {
+ return mParent != null ? mParent.getWidth() : 0;
+ }
+
+ @CalledByNative
+ // Selects the tab corresponding to |actionType| to simulate the user pressing on this tab.
+ private void selectTabForTesting(int actionType) {
+ if (actionType == ActionType.TRANSLATE) {
+ mTabLayout.getTabAt(TARGET_TAB_INDEX).select();
+ } else if (actionType == ActionType.TRANSLATE_SHOW_ORIGINAL) {
+ mTabLayout.getTabAt(SOURCE_TAB_INDEX).select();
+ } else {
+ assert false;
+ }
+ }
+
+ @CalledByNative
+ // Simulates a click of the overflow menu item for "never translate this language."
+ private void clickNeverTranslateLanguageMenuItemForTesting() {
+ onOverflowMenuItemClicked(TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE);
+ }
+
+ @CalledByNative
+ // Simulates a click of the overflow menu item for "never translate this site."
+ private void clickNeverTranslateSiteMenuItemForTesting() {
+ onOverflowMenuItemClicked(TranslateMenu.ID_OVERFLOW_NEVER_SITE);
+ }
+
+ @NativeMethods
+ interface Natives {
+ void applyStringTranslateOption(long nativeTranslateCompactInfoBar,
+ TranslateCompactInfoBar caller, int option, String value);
+ void applyBoolTranslateOption(long nativeTranslateCompactInfoBar,
+ TranslateCompactInfoBar caller, int option, boolean value);
+ boolean shouldAutoNeverTranslate(long nativeTranslateCompactInfoBar,
+ TranslateCompactInfoBar caller, boolean menuExpanded);
+ boolean isIncognito(long nativeTranslateCompactInfoBar, TranslateCompactInfoBar caller);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java
new file mode 100644
index 00000000000..cfb1a06c2f5
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java
@@ -0,0 +1,75 @@
+// Copyright 2017 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.ArrayList;
+import java.util.List;
+
+/**
+ * Translate menu config and its item entity definition.
+ */
+public final class TranslateMenu {
+ /**
+ * The menu item entity.
+ */
+ static final class MenuItem {
+ public final int mType;
+ public final int mId;
+ public final String mCode;
+ public final boolean mWithDivider;
+
+ MenuItem(int itemType, int itemId, boolean withDivider) {
+ this(itemType, itemId, EMPTY_STRING, withDivider);
+ }
+
+ MenuItem(int itemType, int itemId, String code) {
+ this(itemType, itemId, code, false);
+ }
+
+ MenuItem(int itemType, int itemId, String code, boolean withDivider) {
+ mType = itemType;
+ mId = itemId;
+ mCode = code;
+ mWithDivider = withDivider;
+ }
+ }
+
+ public static final String EMPTY_STRING = "";
+
+ // Menu type config.
+ public static final int MENU_OVERFLOW = 0;
+ public static final int MENU_TARGET_LANGUAGE = 1;
+ public static final int MENU_SOURCE_LANGUAGE = 2;
+
+ // Menu item type config.
+ public static final int ITEM_LANGUAGE = 0;
+ public static final int ITEM_CHECKBOX_OPTION = 1;
+ public static final int MENU_ITEM_TYPE_COUNT = 2;
+
+ // Menu Item ID config for MENU_OVERFLOW.
+ public static final int ID_OVERFLOW_MORE_LANGUAGE = 0;
+ public static final int ID_OVERFLOW_ALWAYS_TRANSLATE = 1;
+ public static final int ID_OVERFLOW_NEVER_SITE = 2;
+ public static final int ID_OVERFLOW_NEVER_LANGUAGE = 3;
+ public static final int ID_OVERFLOW_NOT_THIS_LANGUAGE = 4;
+
+ /**
+ * Build overflow menu item list.
+ */
+ static List<MenuItem> getOverflowMenu(boolean isIncognito) {
+ List<MenuItem> menu = new ArrayList<MenuItem>();
+ menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_MORE_LANGUAGE, true));
+ if (!isIncognito) {
+ // "Always translate" does nothing in incognito mode, so just hide it.
+ menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_ALWAYS_TRANSLATE, false));
+ }
+ menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NEVER_LANGUAGE, false));
+ menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NEVER_SITE, false));
+ menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NOT_THIS_LANGUAGE, false));
+ return menu;
+ }
+
+ private TranslateMenu() {}
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java
new file mode 100644
index 00000000000..16454817172
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java
@@ -0,0 +1,321 @@
+// Copyright 2017 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.Rect;
+import android.os.Build;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Helper class for managing the Translate Overflow Menu.
+ */
+public class TranslateMenuHelper implements AdapterView.OnItemClickListener {
+ private final TranslateMenuListener mMenuListener;
+ private final TranslateOptions mOptions;
+
+ private ContextThemeWrapper mContextWrapper;
+ private TranslateMenuAdapter mAdapter;
+ private View mAnchorView;
+ private ListPopupWindow mPopup;
+ private boolean mIsIncognito;
+
+ /**
+ * Interface for receiving the click event of menu item.
+ */
+ public interface TranslateMenuListener {
+ void onOverflowMenuItemClicked(int itemId);
+ void onTargetMenuItemClicked(String code);
+ void onSourceMenuItemClicked(String code);
+ }
+
+ public TranslateMenuHelper(Context context, View anchorView, TranslateOptions options,
+ TranslateMenuListener itemListener, boolean isIncognito) {
+ mContextWrapper = new ContextThemeWrapper(context, R.style.OverflowMenuThemeOverlay);
+ mAnchorView = anchorView;
+ mOptions = options;
+ mMenuListener = itemListener;
+ mIsIncognito = isIncognito;
+ }
+
+ /**
+ * Build translate menu by menu type.
+ */
+ private List<TranslateMenu.MenuItem> getMenuList(int menuType) {
+ List<TranslateMenu.MenuItem> menuList = new ArrayList<TranslateMenu.MenuItem>();
+ if (menuType == TranslateMenu.MENU_OVERFLOW) {
+ // TODO(googleo): Add language short list above static menu after its data is ready.
+ menuList.addAll(TranslateMenu.getOverflowMenu(mIsIncognito));
+ } else {
+ for (int i = 0; i < mOptions.allLanguages().size(); ++i) {
+ String code = mOptions.allLanguages().get(i).mLanguageCode;
+ // Avoid source language in the source language list.
+ if (menuType == TranslateMenu.MENU_SOURCE_LANGUAGE
+ && code.equals(mOptions.sourceLanguageCode())) {
+ continue;
+ }
+ // Avoid target language in the target language list.
+ if (menuType == TranslateMenu.MENU_TARGET_LANGUAGE
+ && code.equals(mOptions.targetLanguageCode())) {
+ continue;
+ }
+ menuList.add(new TranslateMenu.MenuItem(TranslateMenu.ITEM_LANGUAGE, i, code));
+ }
+ }
+ return menuList;
+ }
+
+ /**
+ * Show the overflow menu.
+ * @param menuType The type of overflow menu to show.
+ * @param maxwidth Maximum width of menu. Set to 0 when not specified.
+ */
+ public void show(int menuType, int maxWidth) {
+ if (mPopup == null) {
+ mPopup = new ListPopupWindow(mContextWrapper, null, android.R.attr.popupMenuStyle);
+ mPopup.setModal(true);
+ mPopup.setAnchorView(mAnchorView);
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+
+ // Need to explicitly set the background here. Relying on it being set in the style
+ // caused an incorrectly drawn background.
+ // TODO(martiw): We might need a new menu background here.
+ mPopup.setBackgroundDrawable(
+ ContextCompat.getDrawable(mContextWrapper, R.drawable.popup_bg_tinted));
+
+ mPopup.setOnItemClickListener(this);
+
+ // The menu must be shifted down by the height of the anchor view in order to be
+ // displayed over and above it.
+ int anchorHeight = mAnchorView.getHeight();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ // Setting a positive offset here shifts the menu down.
+ mPopup.setVerticalOffset(anchorHeight);
+ } else {
+ // The framework's PopupWindow positioning changed between N and M. Setting
+ // a negative offset here shifts the menu down rather than up.
+ mPopup.setVerticalOffset(-anchorHeight);
+ }
+
+ mAdapter = new TranslateMenuAdapter(menuType);
+ mPopup.setAdapter(mAdapter);
+ } else {
+ mAdapter.refreshMenu(menuType);
+ }
+
+ if (menuType == TranslateMenu.MENU_OVERFLOW) {
+ // Use measured width when it is a overflow menu.
+ Rect bgPadding = new Rect();
+ mPopup.getBackground().getPadding(bgPadding);
+ int measuredWidth = measureMenuWidth(mAdapter) + bgPadding.left + bgPadding.right;
+ mPopup.setWidth((maxWidth > 0 && measuredWidth > maxWidth) ? maxWidth : measuredWidth);
+ } else {
+ // Use fixed width otherwise.
+ int popupWidth = mContextWrapper.getResources().getDimensionPixelSize(
+ R.dimen.weblayer_infobar_translate_menu_width);
+ mPopup.setWidth(popupWidth);
+ }
+
+ // When layout is RTL, set the horizontal offset to align the menu with the left side of the
+ // screen.
+ if (mAnchorView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ int[] tempLocation = new int[2];
+ mAnchorView.getLocationOnScreen(tempLocation);
+ mPopup.setHorizontalOffset(-tempLocation[0]);
+ }
+
+ if (!mPopup.isShowing()) {
+ mPopup.show();
+ mPopup.getListView().setItemsCanFocus(true);
+ }
+ }
+
+ private int measureMenuWidth(TranslateMenuAdapter adapter) {
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+ final int count = adapter.getCount();
+ int width = 0;
+ int itemType = 0;
+ View itemView = null;
+ for (int i = 0; i < count; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+ itemView = adapter.getView(i, itemView, null);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+ return width;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ dismiss();
+
+ TranslateMenu.MenuItem item = mAdapter.getItem(position);
+ switch (mAdapter.mMenuType) {
+ case TranslateMenu.MENU_OVERFLOW:
+ mMenuListener.onOverflowMenuItemClicked(item.mId);
+ return;
+ case TranslateMenu.MENU_TARGET_LANGUAGE:
+ mMenuListener.onTargetMenuItemClicked(item.mCode);
+ return;
+ case TranslateMenu.MENU_SOURCE_LANGUAGE:
+ mMenuListener.onSourceMenuItemClicked(item.mCode);
+ return;
+ default:
+ assert false : "Unsupported Menu Item Id";
+ }
+ }
+
+ /**
+ * Dismisses the translate option menu.
+ */
+ public void dismiss() {
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
+ /**
+ * @return Whether the menu is currently showing.
+ */
+ public boolean isShowing() {
+ if (mPopup == null) {
+ return false;
+ }
+ return mPopup.isShowing();
+ }
+
+ /**
+ * The provides the views of the menu items and dividers.
+ */
+ private final class TranslateMenuAdapter extends ArrayAdapter<TranslateMenu.MenuItem> {
+ private final LayoutInflater mInflater;
+ private int mMenuType;
+
+ public TranslateMenuAdapter(int menuType) {
+ super(mContextWrapper, R.layout.weblayer_translate_menu_item, getMenuList(menuType));
+ mInflater = LayoutInflater.from(mContextWrapper);
+ mMenuType = menuType;
+ }
+
+ private void refreshMenu(int menuType) {
+ // MENU_OVERFLOW is static and it should not reload.
+ if (menuType == TranslateMenu.MENU_OVERFLOW) return;
+
+ clear();
+
+ mMenuType = menuType;
+ addAll(getMenuList(menuType));
+ notifyDataSetChanged();
+ }
+
+ private String getItemViewText(TranslateMenu.MenuItem item) {
+ if (mMenuType == TranslateMenu.MENU_OVERFLOW) {
+ // Overflow menu items are manually defined one by one.
+ String source = mOptions.sourceLanguageName();
+ switch (item.mId) {
+ case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE:
+ return mContextWrapper.getString(
+ R.string.translate_option_always_translate, source);
+ case TranslateMenu.ID_OVERFLOW_MORE_LANGUAGE:
+ return mContextWrapper.getString(R.string.translate_option_more_language);
+ case TranslateMenu.ID_OVERFLOW_NEVER_SITE:
+ return mContextWrapper.getString(R.string.translate_never_translate_site);
+ case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE:
+ return mContextWrapper.getString(
+ R.string.translate_option_never_translate, source);
+ case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE:
+ return mContextWrapper.getString(
+ R.string.translate_option_not_source_language, source);
+ default:
+ assert false : "Unexpected Overflow Item Id";
+ }
+ } else {
+ // Get source and target language menu items text by language code.
+ return mOptions.getRepresentationFromCode(item.mCode);
+ }
+ return "";
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return getItem(position).mType;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return TranslateMenu.MENU_ITEM_TYPE_COUNT;
+ }
+
+ private View getItemView(
+ View menuItemView, int position, ViewGroup parent, int resourceId) {
+ if (menuItemView == null) {
+ menuItemView = mInflater.inflate(resourceId, parent, false);
+ }
+ ((TextView) menuItemView.findViewById(R.id.weblayer_menu_item_text))
+ .setText(getItemViewText(getItem(position)));
+ return menuItemView;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View menuItemView = convertView;
+ switch (getItemViewType(position)) {
+ case TranslateMenu.ITEM_CHECKBOX_OPTION:
+ menuItemView = getItemView(menuItemView, position, parent,
+ R.layout.weblayer_translate_menu_item_checked);
+
+ ImageView checkboxIcon =
+ menuItemView.findViewById(R.id.weblayer_menu_item_icon);
+ if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE
+ && mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)) {
+ checkboxIcon.setVisibility(View.VISIBLE);
+ } else if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE
+ && mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE)) {
+ checkboxIcon.setVisibility(View.VISIBLE);
+ } else if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_NEVER_SITE
+ && mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN)) {
+ checkboxIcon.setVisibility(View.VISIBLE);
+ } else {
+ checkboxIcon.setVisibility(View.INVISIBLE);
+ }
+
+ View divider =
+ (View) menuItemView.findViewById(R.id.weblayer_menu_item_divider);
+ if (getItem(position).mWithDivider) {
+ divider.setVisibility(View.VISIBLE);
+ }
+ break;
+ case TranslateMenu.ITEM_LANGUAGE:
+ menuItemView = getItemView(
+ menuItemView, position, parent, R.layout.weblayer_translate_menu_item);
+ break;
+ default:
+ assert false : "Unexpected MenuItem type";
+ }
+ return menuItemView;
+ }
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java
new file mode 100644
index 00000000000..ba38d4e26f6
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java
@@ -0,0 +1,278 @@
+// Copyright 2013 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.text.TextUtils;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class that keeps the state of the different translation options and
+ * languages.
+ */
+public class TranslateOptions {
+ /**
+ * A container for Language Code and it's translated representation and it's native UMA
+ * specific hashcode.
+ * For example for Spanish when viewed from a French locale, this will contain es, Espagnol,
+ * 114573335
+ **/
+ public static class TranslateLanguageData {
+ public final String mLanguageCode;
+ public final String mLanguageRepresentation;
+ public final Integer mLanguageUMAHashCode;
+
+ public TranslateLanguageData(
+ String languageCode, String languageRepresentation, Integer uMAhashCode) {
+ assert languageCode != null;
+ assert languageRepresentation != null;
+ mLanguageCode = languageCode;
+ mLanguageRepresentation = languageRepresentation;
+ mLanguageUMAHashCode = uMAhashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TranslateLanguageData)) return false;
+ TranslateLanguageData other = (TranslateLanguageData) obj;
+ return this.mLanguageCode.equals(other.mLanguageCode)
+ && this.mLanguageRepresentation.equals(other.mLanguageRepresentation)
+ && this.mLanguageUMAHashCode.equals(other.mLanguageUMAHashCode);
+ }
+
+ @Override
+ public int hashCode() {
+ return (mLanguageCode + mLanguageRepresentation).hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "mLanguageCode:" + mLanguageCode + " - mlanguageRepresentation "
+ + mLanguageRepresentation + " - mLanguageUMAHashCode " + mLanguageUMAHashCode;
+ }
+ }
+
+ // Values must be numerated from 0 and can't have gaps
+ // (they're used for indexing mOptions).
+ @IntDef({Type.NEVER_LANGUAGE, Type.NEVER_DOMAIN, Type.ALWAYS_LANGUAGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {
+ int NEVER_LANGUAGE = 0;
+ int NEVER_DOMAIN = 1;
+ int ALWAYS_LANGUAGE = 2;
+
+ int NUM_ENTRIES = 3;
+ }
+
+ private String mSourceLanguageCode;
+ private String mTargetLanguageCode;
+
+ private final ArrayList<TranslateLanguageData> mAllLanguages;
+
+ // language code to translated language name map
+ // Conceptually final
+ private Map<String, String> mCodeToRepresentation;
+
+ // Language code to its UMA hashcode representation.
+ private Map<String, Integer> mCodeToUMAHashCode;
+
+ // Will reflect the state before the object was ever modified
+ private final boolean[] mOriginalOptions;
+
+ private final String mOriginalSourceLanguageCode;
+ private final String mOriginalTargetLanguageCode;
+ private final boolean mTriggeredFromMenu;
+
+ private final boolean[] mOptions;
+
+ private TranslateOptions(String sourceLanguageCode, String targetLanguageCode,
+ ArrayList<TranslateLanguageData> allLanguages, boolean neverLanguage,
+ boolean neverDomain, boolean alwaysLanguage, boolean triggeredFromMenu,
+ boolean[] originalOptions) {
+ assert Type.NUM_ENTRIES == 3;
+ mOptions = new boolean[Type.NUM_ENTRIES];
+ mOptions[Type.NEVER_LANGUAGE] = neverLanguage;
+ mOptions[Type.NEVER_DOMAIN] = neverDomain;
+ mOptions[Type.ALWAYS_LANGUAGE] = alwaysLanguage;
+
+ mOriginalOptions = originalOptions == null ? mOptions.clone() : originalOptions.clone();
+
+ mSourceLanguageCode = sourceLanguageCode;
+ mTargetLanguageCode = targetLanguageCode;
+ mOriginalSourceLanguageCode = mSourceLanguageCode;
+ mOriginalTargetLanguageCode = mTargetLanguageCode;
+ mTriggeredFromMenu = triggeredFromMenu;
+
+ mAllLanguages = allLanguages;
+ mCodeToRepresentation = new HashMap<String, String>();
+ mCodeToUMAHashCode = new HashMap<String, Integer>();
+ for (TranslateLanguageData language : allLanguages) {
+ mCodeToRepresentation.put(language.mLanguageCode, language.mLanguageRepresentation);
+ mCodeToUMAHashCode.put(language.mLanguageCode, language.mLanguageUMAHashCode);
+ }
+ }
+
+ /**
+ * Creates a TranslateOptions by the given data.
+ */
+ public static TranslateOptions create(String sourceLanguageCode, String targetLanguageCode,
+ String[] languages, String[] codes, boolean alwaysTranslate, boolean triggeredFromMenu,
+ int[] hashCodes) {
+ assert languages.length == codes.length;
+
+ ArrayList<TranslateLanguageData> languageList = new ArrayList<TranslateLanguageData>();
+ for (int i = 0; i < languages.length; ++i) {
+ Integer hashCode = hashCodes != null ? Integer.valueOf(hashCodes[i]) : null;
+ languageList.add(new TranslateLanguageData(codes[i], languages[i], hashCode));
+ }
+ return new TranslateOptions(sourceLanguageCode, targetLanguageCode, languageList, false,
+ false, alwaysTranslate, triggeredFromMenu, null);
+ }
+
+ /**
+ * Returns a copy of the current instance.
+ */
+ TranslateOptions copy() {
+ return new TranslateOptions(mSourceLanguageCode, mTargetLanguageCode, mAllLanguages,
+ mOptions[Type.NEVER_LANGUAGE], mOptions[Type.NEVER_DOMAIN],
+ mOptions[Type.ALWAYS_LANGUAGE], mTriggeredFromMenu, mOriginalOptions);
+ }
+
+ public String sourceLanguageName() {
+ return getRepresentationFromCode(mSourceLanguageCode);
+ }
+
+ public String targetLanguageName() {
+ return getRepresentationFromCode(mTargetLanguageCode);
+ }
+
+ public String sourceLanguageCode() {
+ return mSourceLanguageCode;
+ }
+
+ public String targetLanguageCode() {
+ return mTargetLanguageCode;
+ }
+
+ public boolean triggeredFromMenu() {
+ return mTriggeredFromMenu;
+ }
+
+ public boolean optionsChanged() {
+ return (!mSourceLanguageCode.equals(mOriginalSourceLanguageCode))
+ || (!mTargetLanguageCode.equals(mOriginalTargetLanguageCode))
+ || (mOptions[Type.NEVER_LANGUAGE] != mOriginalOptions[Type.NEVER_LANGUAGE])
+ || (mOptions[Type.NEVER_DOMAIN] != mOriginalOptions[Type.NEVER_DOMAIN])
+ || (mOptions[Type.ALWAYS_LANGUAGE] != mOriginalOptions[Type.ALWAYS_LANGUAGE]);
+ }
+
+ public List<TranslateLanguageData> allLanguages() {
+ return mAllLanguages;
+ }
+
+ public boolean getTranslateState(@Type int type) {
+ return mOptions[type];
+ }
+
+ public boolean setSourceLanguage(String languageCode) {
+ boolean canSet = canSetLanguage(languageCode, mTargetLanguageCode);
+ if (canSet) mSourceLanguageCode = languageCode;
+ return canSet;
+ }
+
+ public boolean setTargetLanguage(String languageCode) {
+ boolean canSet = canSetLanguage(mSourceLanguageCode, languageCode);
+ if (canSet) mTargetLanguageCode = languageCode;
+ return canSet;
+ }
+
+ /**
+ * Sets the new state of never translate domain.
+ *
+ * @return true if the toggling was possible
+ */
+ public void toggleNeverTranslateDomainState(boolean value) {
+ mOptions[Type.NEVER_DOMAIN] = value;
+ }
+
+ /**
+ * Sets the new state of never translate language.
+ *
+ * @return true if the toggling was possible
+ */
+ public boolean toggleNeverTranslateLanguageState(boolean value) {
+ // Do not toggle if we are activating NeverLanguage but AlwaysTranslate
+ // for a language pair with the same source language is already active.
+ if (mOptions[Type.ALWAYS_LANGUAGE] && value) return false;
+ mOptions[Type.NEVER_LANGUAGE] = value;
+ return true;
+ }
+
+ /**
+ * Sets the new state of never translate a language pair.
+ *
+ * @return true if the toggling was possible
+ */
+ public boolean toggleAlwaysTranslateLanguageState(boolean value) {
+ // Do not toggle if we are activating AlwaysLanguage but NeverLanguage is active already.
+ if (mOptions[Type.NEVER_LANGUAGE] && value) return false;
+ mOptions[Type.ALWAYS_LANGUAGE] = value;
+ return true;
+ }
+
+ /**
+ * Gets the language's translated representation from a given language code.
+ * @param languageCode ISO code for the language
+ * @return The translated representation of the language, or "" if not found.
+ */
+ public String getRepresentationFromCode(String languageCode) {
+ return isValidLanguageCode(languageCode) ? mCodeToRepresentation.get(languageCode) : "";
+ }
+
+ /**
+ * Gets the language's UMA hashcode representation from a given language code.
+ * @param languageCode ISO code for the language
+ * @return The UMA hashcode representation of the language, or null if not found.
+ */
+ public Integer getUMAHashCodeFromCode(String languageCode) {
+ return isValidLanguageUMAHashCode(languageCode) ? mCodeToUMAHashCode.get(languageCode)
+ : null;
+ }
+
+ private boolean isValidLanguageCode(String languageCode) {
+ return !TextUtils.isEmpty(languageCode) && mCodeToRepresentation.containsKey(languageCode);
+ }
+
+ private boolean isValidLanguageUMAHashCode(String languageCode) {
+ return !TextUtils.isEmpty(languageCode) && mCodeToUMAHashCode.containsKey(languageCode);
+ }
+
+ private boolean canSetLanguage(String sourceCode, String targetCode) {
+ return isValidLanguageCode(sourceCode) && isValidLanguageCode(targetCode);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append(sourceLanguageCode())
+ .append(" -> ")
+ .append(targetLanguageCode())
+ .append(" - ")
+ .append("Never Language:")
+ .append(mOptions[Type.NEVER_LANGUAGE])
+ .append(" Always Language:")
+ .append(mOptions[Type.ALWAYS_LANGUAGE])
+ .append(" Never Domain:")
+ .append(mOptions[Type.NEVER_DOMAIN])
+ .toString();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java
new file mode 100644
index 00000000000..4cde0b46193
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java
@@ -0,0 +1,63 @@
+// Copyright 2017 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.ColorStateList;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * The content of the tab shown in the TranslateTabLayout.
+ */
+public class TranslateTabContent extends FrameLayout {
+ private TextView mTextView;
+ private ProgressBar mProgressBar;
+
+ /**
+ * Constructor for inflating from XML.
+ */
+ public TranslateTabContent(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTextView = (TextView) findViewById(R.id.weblayer_translate_infobar_tab_text);
+ mProgressBar = (ProgressBar) findViewById(R.id.weblayer_translate_infobar_tab_progressbar);
+ }
+
+ /**
+ * Sets the text color for all the states (normal, selected, focused) to be this color.
+ * @param colors The color state list of the title text.
+ */
+ public void setTextColor(ColorStateList colors) {
+ mTextView.setTextColor(colors);
+ }
+
+ /**
+ * Set the title text for this tab.
+ * @param tabTitle The new title string.
+ */
+ public void setText(CharSequence tabTitle) {
+ mTextView.setText(tabTitle);
+ }
+
+ /** Hide progress bar and show text. */
+ public void hideProgressBar() {
+ mProgressBar.setVisibility(View.INVISIBLE);
+ mTextView.setVisibility(View.VISIBLE);
+ }
+
+ /** Show progress bar and hide text. */
+ public void showProgressBar() {
+ mTextView.setVisibility(View.INVISIBLE);
+ mProgressBar.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java
new file mode 100644
index 00000000000..37c27f37cc6
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java
@@ -0,0 +1,240 @@
+// Copyright 2017 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.chromium.base.StrictModeContext;
+import org.chromium.components.browser_ui.widget.animation.Interpolators;
+
+/**
+ * TabLayout shown in the TranslateCompactInfoBar.
+ */
+public class TranslateTabLayout extends TabLayout {
+ /** The tab in which a spinning progress bar is showing. */
+ private Tab mTabShowingProgressBar;
+
+ /** The amount of waiting time before starting the scrolling animation. */
+ private static final long START_POSITION_WAIT_DURATION_MS = 1000;
+
+ /** The amount of time it takes to scroll to the end during the scrolling animation. */
+ private static final long SCROLL_DURATION_MS = 300;
+
+ /** We define the keyframes of the scrolling animation in this object. */
+ ObjectAnimator mScrollToEndAnimator;
+
+ /** Start padding of a Tab. Used for width calculation only. Will not be applied to views. */
+ private int mTabPaddingStart;
+
+ /** End padding of a Tab. Used for width calculation only. Will not be applied to views. */
+ private int mTabPaddingEnd;
+
+ /**
+ * Constructor for inflating from XML.
+ */
+ @SuppressLint("CustomViewStyleable") // TODO(crbug.com/807725): Remove and fix.
+ public TranslateTabLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.TabLayout, 0, R.style.Widget_Design_TabLayout);
+ mTabPaddingStart = mTabPaddingEnd =
+ a.getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);
+ mTabPaddingStart =
+ a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart, mTabPaddingStart);
+ mTabPaddingEnd =
+ a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd, mTabPaddingEnd);
+ }
+
+ /**
+ * Add new Tabs with title strings.
+ * @param titles Titles of the tabs to be added.
+ */
+ public void addTabs(CharSequence... titles) {
+ for (CharSequence title : titles) {
+ addTabWithTitle(title);
+ }
+ }
+
+ /**
+ * Add a new Tab with the title string.
+ * @param tabTitle Title string of the new tab.
+ */
+ public void addTabWithTitle(CharSequence tabTitle) {
+ TranslateTabContent tabContent;
+ // LayoutInflater may trigger accessing the disk.
+ try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+ tabContent =
+ (TranslateTabContent) LayoutInflater.from(getContext())
+ .inflate(R.layout.weblayer_infobar_translate_tab_content, this, false);
+ }
+ // Set text color using tabLayout's ColorStateList. So that the title text will change
+ // color when selected and unselected.
+ tabContent.setTextColor(getTabTextColors());
+ tabContent.setText(tabTitle);
+
+ Tab tab = newTab();
+ tab.setCustomView(tabContent);
+ tab.setContentDescription(tabTitle);
+ super.addTab(tab);
+ }
+
+ /**
+ * Replace the title string of a tab.
+ * @param tabPos The position of the tab to modify.
+ * @param tabTitle The new title string.
+ */
+ public void replaceTabTitle(int tabPos, CharSequence tabTitle) {
+ if (tabPos < 0 || tabPos >= getTabCount()) {
+ return;
+ }
+ Tab tab = getTabAt(tabPos);
+ ((TranslateTabContent) tab.getCustomView()).setText(tabTitle);
+ tab.setContentDescription(tabTitle);
+ }
+
+ /**
+ * Show the spinning progress bar on a specified tab.
+ * @param tabPos The position of the tab to show the progress bar.
+ */
+ public void showProgressBarOnTab(int tabPos) {
+ if (tabPos < 0 || tabPos >= getTabCount() || mTabShowingProgressBar != null) {
+ return;
+ }
+ mTabShowingProgressBar = getTabAt(tabPos);
+
+ // TODO(martiw) See if we need to setContentDescription as "Translating" here.
+
+ if (tabIsSupported(mTabShowingProgressBar)) {
+ ((TranslateTabContent) mTabShowingProgressBar.getCustomView()).showProgressBar();
+ }
+ }
+
+ /**
+ * Hide the spinning progress bar in the tabs.
+ */
+ public void hideProgressBar() {
+ if (mTabShowingProgressBar == null) return;
+
+ if (tabIsSupported(mTabShowingProgressBar)) {
+ ((TranslateTabContent) mTabShowingProgressBar.getCustomView()).hideProgressBar();
+ }
+
+ mTabShowingProgressBar = null;
+ }
+
+ // Overridden to block children's touch event when showing progress bar.
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Allow touches to propagate to children only if the layout can be interacted with.
+ if (mTabShowingProgressBar != null) {
+ return true;
+ }
+ endScrollingAnimationIfPlaying();
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ /** Check if the tab is supported in TranslateTabLayout. */
+ private boolean tabIsSupported(Tab tab) {
+ return (tab.getCustomView() instanceof TranslateTabContent);
+ }
+
+ // Overridden to make sure only supported Tabs can be added.
+ @Override
+ public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
+ if (!tabIsSupported(tab)) {
+ throw new IllegalArgumentException();
+ }
+ super.addTab(tab, position, setSelected);
+ }
+
+ // Overrided to make sure only supported Tabs can be added.
+ @Override
+ public void addTab(@NonNull Tab tab, boolean setSelected) {
+ if (!tabIsSupported(tab)) {
+ throw new IllegalArgumentException();
+ }
+ super.addTab(tab, setSelected);
+ }
+
+ /**
+ * Calculate and return the width of a specified tab. Tab doesn't provide a means of getting
+ * the width so we need to calculate the width by summing up the tab paddings and content width.
+ * @param position Tab position.
+ * @return Tab's width in pixels.
+ */
+ private int getTabWidth(int position) {
+ if (getTabAt(position) == null) return 0;
+ return getTabAt(position).getCustomView().getWidth() + mTabPaddingStart + mTabPaddingEnd;
+ }
+
+ /**
+ * Calculate the total width of all tabs and return it.
+ * @return Total width of all tabs in pixels.
+ */
+ private int getTabsTotalWidth() {
+ int totalWidth = 0;
+ for (int i = 0; i < getTabCount(); i++) {
+ totalWidth += getTabWidth(i);
+ }
+ return totalWidth;
+ }
+
+ /**
+ * Calculate the maximum scroll distance (by subtracting layout width from total width of tabs)
+ * and return it.
+ * @return Maximum scroll distance in pixels.
+ */
+ private int maxScrollDistance() {
+ int scrollDistance = getTabsTotalWidth() - getWidth();
+ return scrollDistance > 0 ? scrollDistance : 0;
+ }
+
+ /**
+ * Perform the scrolling animation if this tablayout has any scrollable distance.
+ */
+ // TODO(crbug.com/900912): Figure out whether setScrollX is actually available.
+ @SuppressLint("ObjectAnimatorBinding")
+ public void startScrollingAnimationIfNeeded() {
+ int maxScrollDistance = maxScrollDistance();
+ if (maxScrollDistance == 0) {
+ return;
+ }
+ // The steps of the scrolling animation:
+ // 1. wait for START_POSITION_WAIT_DURATION_MS.
+ // 2. scroll to the end in SCROLL_DURATION_MS.
+ mScrollToEndAnimator = ObjectAnimator.ofInt(this, "scrollX",
+ getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : maxScrollDistance);
+ mScrollToEndAnimator.setStartDelay(START_POSITION_WAIT_DURATION_MS);
+ mScrollToEndAnimator.setDuration(SCROLL_DURATION_MS);
+ mScrollToEndAnimator.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR);
+ mScrollToEndAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrollToEndAnimator = null;
+ }
+ });
+ mScrollToEndAnimator.start();
+ }
+
+ /**
+ * End the scrolling animation if it is playing.
+ */
+ public void endScrollingAnimationIfPlaying() {
+ if (mScrollToEndAnimator != null) mScrollToEndAnimator.end();
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
index 31a6a5484b2..4f3d7b439f1 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
@@ -28,6 +28,7 @@ import org.chromium.components.omnibox.SecurityButtonAnimationDelegate;
import org.chromium.components.omnibox.SecurityStatusIcon;
import org.chromium.components.page_info.PageInfoController;
import org.chromium.components.page_info.PermissionParamsListBuilderDelegate;
+import org.chromium.content_public.browser.WebContents;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
import org.chromium.weblayer_private.interfaces.IUrlBarController;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
@@ -159,20 +160,19 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
ContextCompat.getColor(embedderContext, mUrlIconColor)));
}
- mSecurityButton.setOnClickListener(v -> { showPageInfoUi(v); });
if (mShowPageInfoWhenUrlTextClicked) {
- mUrlTextView.setOnClickListener(v -> { showPageInfoUi(v); });
+ setOnClickListener(v -> { showPageInfoUi(v); });
+ } else {
+ mSecurityButton.setOnClickListener(v -> { showPageInfoUi(v); });
}
}
private void showPageInfoUi(View v) {
+ WebContents webContents = mBrowserImpl.getActiveTab().getWebContents();
PageInfoController.show(mBrowserImpl.getWindowAndroid().getActivity().get(),
- mBrowserImpl.getActiveTab().getWebContents(),
+ webContents,
/* contentPublisher= */ null, PageInfoController.OpenedFromSource.TOOLBAR,
- new PageInfoControllerDelegateImpl(mBrowserImpl.getContext(),
- mBrowserImpl.getProfile().getName(),
- mBrowserImpl.getActiveTab().getWebContents().getVisibleUrl(),
- mBrowserImpl.getWindowAndroid()::getModalDialogManager),
+ PageInfoControllerDelegateImpl.create(webContents),
new PermissionParamsListBuilderDelegate(mBrowserImpl.getProfile()));
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerAccessibilityUtil.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerAccessibilityUtil.java
index 9054076d7b4..6cce5a8b27d 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerAccessibilityUtil.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerAccessibilityUtil.java
@@ -4,6 +4,8 @@
package org.chromium.weblayer_private;
+import org.chromium.ui.util.AccessibilityUtil;
+
/**
* Exposes information about the current accessibility state.
*/
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java
index ad83409a452..9e79ff718ec 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java
@@ -12,6 +12,7 @@ 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.StrictModeWorkaround;
+import org.chromium.weblayer_private.interfaces.WebLayerVersionConstants;
/**
* Factory used to create WebLayer as well as verify compatibility.
@@ -49,7 +50,13 @@ public final class WebLayerFactoryImpl extends IWebLayerFactory.Stub {
@Override
public boolean isClientSupported() {
StrictModeWorkaround.apply();
- return Math.abs(sClientMajorVersion - getImplementationMajorVersion()) <= 4;
+ int implMajorVersion = getImplementationMajorVersion();
+ // While the client always calls this method, the most recently shipped product gets to
+ // decide compatibility. If we instead let the implementation always decide, then we would
+ // not be able to change the allowed skew of older implementations, even if the client could
+ // support it.
+ if (sClientMajorVersion > implMajorVersion) return true;
+ return implMajorVersion - sClientMajorVersion <= WebLayerVersionConstants.MAX_SKEW;
}
/**
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 7150b1d5ca7..c9223c62146 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -4,6 +4,7 @@
package org.chromium.weblayer_private;
+import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -321,6 +322,9 @@ public final class WebLayerImpl extends IWebLayer.Stub {
public void onReceivedBroadcast(IObjectWrapper appContextWrapper, Intent intent) {
StrictModeWorkaround.apply();
Context context = ObjectWrapper.unwrap(appContextWrapper, Context.class);
+
+ if (IntentUtils.handleIntent(intent)) return;
+
if (intent.getAction().startsWith(DownloadImpl.getIntentPrefix())) {
DownloadImpl.forwardIntent(context, intent, mProfileManager);
} else if (intent.getAction().startsWith(MediaStreamManager.getIntentPrefix())) {
@@ -329,6 +333,19 @@ public final class WebLayerImpl extends IWebLayer.Stub {
}
@Override
+ public void onMediaSessionServiceStarted(IObjectWrapper sessionService, Intent intent) {
+ StrictModeWorkaround.apply();
+ MediaSessionManager.serviceStarted(
+ ObjectWrapper.unwrap(sessionService, Service.class), intent);
+ }
+
+ @Override
+ public void onMediaSessionServiceDestroyed() {
+ StrictModeWorkaround.apply();
+ MediaSessionManager.serviceDestroyed();
+ }
+
+ @Override
public void enumerateAllProfileNames(IObjectWrapper valueCallback) {
StrictModeWorkaround.apply();
final ValueCallback<String[]> callback =
@@ -380,6 +397,30 @@ public final class WebLayerImpl extends IWebLayer.Stub {
}
}
+ public static Intent createMediaSessionServiceIntent() {
+ if (sClient == null) {
+ throw new IllegalStateException("WebLayer should have been initialized already.");
+ }
+
+ try {
+ return sClient.createMediaSessionServiceIntent();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ public static int getMediaSessionNotificationId() {
+ if (sClient == null) {
+ throw new IllegalStateException("WebLayer should have been initialized already.");
+ }
+
+ try {
+ return sClient.getMediaSessionNotificationId();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
public static String getClientApplicationName() {
Context context = ContextUtils.getApplicationContext();
return new StringBuilder()
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java
index f591a671aa2..50984bb6403 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java
@@ -4,19 +4,37 @@
package org.chromium.weblayer_private;
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.webkit.WebViewFactory;
+import androidx.annotation.NonNull;
+
+import org.chromium.base.ContextUtils;
import org.chromium.components.browser_ui.notifications.ChromeNotificationBuilder;
import org.chromium.components.browser_ui.notifications.NotificationBuilder;
+import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
import org.chromium.components.browser_ui.notifications.NotificationMetadata;
import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
/** A notification builder for WebLayer which has extra logic to make icons work correctly. */
final class WebLayerNotificationBuilder extends NotificationBuilder {
- public WebLayerNotificationBuilder(Context context, String channelId,
+ /** Creates a notification builder. */
+ public static WebLayerNotificationBuilder create(
+ @WebLayerNotificationChannels.ChannelId String channelId,
+ @NonNull NotificationMetadata metadata) {
+ Context appContext = ContextUtils.getApplicationContext();
+ ChannelsInitializer initializer =
+ new ChannelsInitializer(new NotificationManagerProxyImpl(appContext),
+ WebLayerNotificationChannels.getInstance(), appContext.getResources());
+ return new WebLayerNotificationBuilder(appContext, channelId, initializer, metadata);
+ }
+
+ private WebLayerNotificationBuilder(Context context, String channelId,
ChannelsInitializer channelsInitializer, NotificationMetadata metadata) {
super(context, channelId, channelsInitializer, metadata);
}
@@ -25,17 +43,67 @@ final class WebLayerNotificationBuilder extends NotificationBuilder {
public ChromeNotificationBuilder setSmallIcon(int icon) {
if (WebLayerImpl.isAndroidResource(icon)) {
super.setSmallIcon(icon);
- return this;
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ super.setSmallIcon(createIcon(icon));
+ } else {
+ // Some fallback is required, or the notification won't appear.
+ super.setSmallIcon(getFallbackAndroidResource(icon));
}
+ return this;
+ }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- super.setSmallIcon(
- Icon.createWithResource(WebViewFactory.getLoadedPackageInfo().packageName,
- WebLayerImpl.getResourceIdForSystemUi(icon)));
+ @Override
+ @SuppressWarnings("deprecation")
+ public ChromeNotificationBuilder addAction(int icon, CharSequence title, PendingIntent intent) {
+ if (WebLayerImpl.isAndroidResource(icon)) {
+ super.addAction(icon, title, intent);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ super.addAction(
+ new Notification.Action.Builder(createIcon(icon), title, intent).build());
} else {
- // Some fallback is required, or the notification won't appear.
- super.setSmallIcon(android.R.drawable.radiobutton_on_background);
+ super.addAction(getFallbackAndroidResource(icon), title, intent);
}
return this;
}
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private Icon createIcon(int resId) {
+ return Icon.createWithResource(WebViewFactory.getLoadedPackageInfo().packageName,
+ WebLayerImpl.getResourceIdForSystemUi(resId));
+ }
+
+ /**
+ * Finds a reasonable replacement for the given app-defined resource from among stock android
+ * resources. This is useful when {@link Icon} is not available.
+ */
+ private int getFallbackAndroidResource(int appResourceId) {
+ if (appResourceId == R.drawable.ic_play_arrow_white_36dp) {
+ return android.R.drawable.ic_media_play;
+ }
+ if (appResourceId == R.drawable.ic_pause_white_36dp) {
+ return android.R.drawable.ic_media_pause;
+ }
+ if (appResourceId == R.drawable.ic_stop_white_36dp) {
+ // There's no ic_media_stop. This standin is at least a square. In practice this
+ // shouldn't ever come up as stop is only used in (Chrome) cast notifications.
+ return android.R.drawable.checkbox_off_background;
+ }
+ if (appResourceId == R.drawable.ic_skip_previous_white_36dp) {
+ return android.R.drawable.ic_media_previous;
+ }
+ if (appResourceId == R.drawable.ic_skip_next_white_36dp) {
+ return android.R.drawable.ic_media_next;
+ }
+ if (appResourceId == R.drawable.ic_fast_forward_white_36dp) {
+ return android.R.drawable.ic_media_ff;
+ }
+ if (appResourceId == R.drawable.ic_fast_rewind_white_36dp) {
+ return android.R.drawable.ic_media_rew;
+ }
+ if (appResourceId == R.drawable.audio_playing) {
+ return android.R.drawable.ic_lock_silent_mode_off;
+ }
+
+ return android.R.drawable.radiobutton_on_background;
+ }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
index 264421f20eb..5967d90d6ec 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationChannels.java
@@ -51,12 +51,13 @@ class WebLayerNotificationChannels extends ChannelDefinitions {
* channel, remove the ID from this StringDef, remove its entry from Predefined Channels.MAP,
* and add it to the return value of {@link #getLegacyChannelIds()}.
*/
- @StringDef({ChannelId.ACTIVE_DOWNLOADS, ChannelId.COMPLETED_DOWNLOADS,
+ @StringDef({ChannelId.ACTIVE_DOWNLOADS, ChannelId.COMPLETED_DOWNLOADS, ChannelId.MEDIA_PLAYBACK,
ChannelId.WEBRTC_CAM_AND_MIC})
@Retention(RetentionPolicy.SOURCE)
public @interface ChannelId {
String ACTIVE_DOWNLOADS = "org.chromium.weblayer.active_downloads";
String COMPLETED_DOWNLOADS = "org.chromium.weblayer.completed_downloads";
+ String MEDIA_PLAYBACK = "org.chromium.weblayer.media_playback";
String WEBRTC_CAM_AND_MIC = "org.chromium.weblayer.webrtc_cam_and_mic";
}
@@ -81,6 +82,10 @@ class WebLayerNotificationChannels extends ChannelDefinitions {
PredefinedChannel.create(ChannelId.COMPLETED_DOWNLOADS,
R.string.notification_category_completed_downloads,
NotificationManager.IMPORTANCE_LOW, ChannelGroupId.WEBLAYER));
+ map.put(ChannelId.MEDIA_PLAYBACK,
+ PredefinedChannel.create(ChannelId.MEDIA_PLAYBACK,
+ R.string.notification_category_media_playback,
+ NotificationManager.IMPORTANCE_LOW, ChannelGroupId.WEBLAYER));
map.put(ChannelId.WEBRTC_CAM_AND_MIC,
PredefinedChannel.create(ChannelId.WEBRTC_CAM_AND_MIC,
R.string.notification_category_webrtc_cam_and_mic,
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java
index d047261bcb4..cabbef5f87a 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java
@@ -11,8 +11,6 @@ import androidx.annotation.Nullable;
import androidx.preference.Preference;
import org.chromium.base.Callback;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory.Type;
import org.chromium.components.browser_ui.site_settings.SiteSettingsClient;
@@ -28,7 +26,6 @@ import java.util.Set;
/**
* A SiteSettingsClient instance that contains WebLayer-specific Site Settings logic.
*/
-@JNINamespace("weblayer")
public class WebLayerSiteSettingsClient
implements SiteSettingsClient, ManagedPreferenceDelegate, SiteSettingsHelpClient,
SiteSettingsPrefClient, WebappSettingsClient {
@@ -75,8 +72,8 @@ public class WebLayerSiteSettingsClient
public boolean isCategoryVisible(@Type int type) {
return type == Type.ALL_SITES || type == Type.AUTOMATIC_DOWNLOADS || type == Type.CAMERA
|| type == Type.COOKIES || type == Type.DEVICE_LOCATION || type == Type.JAVASCRIPT
- || type == Type.MICROPHONE || type == Type.PROTECTED_MEDIA || type == Type.SOUND
- || type == Type.USE_STORAGE;
+ || type == Type.MICROPHONE || type == Type.POPUPS || type == Type.PROTECTED_MEDIA
+ || type == Type.SOUND || type == Type.USE_STORAGE;
}
@Override
@@ -122,32 +119,6 @@ public class WebLayerSiteSettingsClient
public void launchProtectedContentHelpAndFeedbackActivity(Activity currentActivity) {}
// SiteSettingsPrefClient implementation:
- // TODO(crbug.com/1071603): Once PrefServiceBridge is componentized we can get rid of the JNI
- // methods here and call PrefServiceBridge directly.
-
- @Override
- public boolean getBlockThirdPartyCookies() {
- return WebLayerSiteSettingsClientJni.get().getBlockThirdPartyCookies(mBrowserContextHandle);
- }
- @Override
- public void setBlockThirdPartyCookies(boolean newValue) {
- WebLayerSiteSettingsClientJni.get().setBlockThirdPartyCookies(
- mBrowserContextHandle, newValue);
- }
- @Override
- public boolean isBlockThirdPartyCookiesManaged() {
- // WebLayer doesn't support managed prefs.
- return false;
- }
-
- @Override
- public int getCookieControlsMode() {
- return WebLayerSiteSettingsClientJni.get().getCookieControlsMode(mBrowserContextHandle);
- }
- @Override
- public void setCookieControlsMode(int newValue) {
- WebLayerSiteSettingsClientJni.get().setCookieControlsMode(mBrowserContextHandle, newValue);
- }
// The quiet notification UI is a Chrome-specific feature for now.
@Override
@@ -187,13 +158,4 @@ public class WebLayerSiteSettingsClient
public String getNotificationDelegatePackageNameForOrigin(Origin origin) {
return null;
}
-
- @NativeMethods
- interface Natives {
- boolean getBlockThirdPartyCookies(BrowserContextHandle browserContextHandle);
- void setBlockThirdPartyCookies(BrowserContextHandle browserContextHandle, boolean newValue);
-
- int getCookieControlsMode(BrowserContextHandle browserContextHandle);
- void setCookieControlsMode(BrowserContextHandle browserContextHandle, int newValue);
- }
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java
new file mode 100644
index 00000000000..d639a42cf3a
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java
@@ -0,0 +1,76 @@
+// Copyright 2020 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.APICallException;
+import org.chromium.weblayer_private.interfaces.IWebMessageCallbackClient;
+import org.chromium.weblayer_private.interfaces.IWebMessageReplyProxy;
+
+/**
+ * WebMessageReplyProxyImpl is responsible for both sending and receiving WebMessages.
+ */
+@JNINamespace("weblayer")
+public final class WebMessageReplyProxyImpl extends IWebMessageReplyProxy.Stub {
+ private long mNativeWebMessageReplyProxyImpl;
+ private final IWebMessageCallbackClient mClient;
+ // Unique id (scoped to the call to Tab.registerWebMessageCallback()) for this proxy. This is
+ // sent over AIDL.
+ private final int mId;
+
+ private WebMessageReplyProxyImpl(long nativeWebMessageReplyProxyImpl, int id,
+ IWebMessageCallbackClient client, boolean isMainFrame, String sourceOrigin) {
+ mNativeWebMessageReplyProxyImpl = nativeWebMessageReplyProxyImpl;
+ mClient = client;
+ mId = id;
+ try {
+ client.onNewReplyProxy(this, mId, isMainFrame, sourceOrigin);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ @CalledByNative
+ private static WebMessageReplyProxyImpl create(long nativeWebMessageReplyProxyImpl, int id,
+ IWebMessageCallbackClient client, boolean isMainFrame, String sourceOrigin) {
+ return new WebMessageReplyProxyImpl(
+ nativeWebMessageReplyProxyImpl, id, client, isMainFrame, sourceOrigin);
+ }
+
+ @CalledByNative
+ private void onNativeDestroyed() {
+ mNativeWebMessageReplyProxyImpl = 0;
+ try {
+ mClient.onReplyProxyDestroyed(mId);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ @CalledByNative
+ private void onPostMessage(String message) {
+ try {
+ mClient.onPostMessage(mId, message);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ @Override
+ public void postMessage(String message) {
+ if (mNativeWebMessageReplyProxyImpl != 0) {
+ WebMessageReplyProxyImplJni.get().postMessage(mNativeWebMessageReplyProxyImpl, message);
+ }
+ }
+
+ @NativeMethods
+ interface Natives {
+ void postMessage(long nativeWebMessageReplyProxyImpl, String message);
+ }
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebShareServiceFactory.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebShareServiceFactory.java
new file mode 100644
index 00000000000..2005e53d2fd
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebShareServiceFactory.java
@@ -0,0 +1,40 @@
+// Copyright 2020 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.browser_ui.share.ShareHelper;
+import org.chromium.components.browser_ui.share.ShareParams;
+import org.chromium.components.browser_ui.webshare.ShareServiceImpl;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.services.service_manager.InterfaceFactory;
+import org.chromium.webshare.mojom.ShareService;
+
+/**
+ * Factory that creates instances of ShareService.
+ */
+public class WebShareServiceFactory implements InterfaceFactory<ShareService> {
+ private final WebContents mWebContents;
+
+ public WebShareServiceFactory(WebContents webContents) {
+ mWebContents = webContents;
+ }
+
+ @Override
+ public ShareService createImpl() {
+ ShareServiceImpl.WebShareDelegate delegate = new ShareServiceImpl.WebShareDelegate() {
+ @Override
+ public boolean canShare() {
+ return mWebContents.getTopLevelNativeWindow().getActivity() != null;
+ }
+
+ @Override
+ public void share(ShareParams params) {
+ ShareHelper.shareWithUi(params);
+ }
+ };
+
+ return new ShareServiceImpl(mWebContents, delegate);
+ }
+}
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
index 8093e226baa..ed9a123fe9c 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
@@ -34,4 +34,6 @@ interface IBrowser {
IUrlBarController getUrlBarController() = 9;
void setBottomView(in IObjectWrapper view) = 10;
+
+ ITab createTab() = 11;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
index 3a21e9b518b..653b9a6af12 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl
@@ -33,4 +33,7 @@ interface INavigationController {
// Added in 82, removed in 83.
// void replace(in String uri) = 12;
+
+ // Added in 85.
+ boolean isNavigationEntrySkippable(int index) = 13;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl
index 60132f87c71..73432f8bd19 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl
@@ -6,6 +6,7 @@ package org.chromium.weblayer_private.interfaces;
import org.chromium.weblayer_private.interfaces.IClientNavigation;
import org.chromium.weblayer_private.interfaces.INavigation;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
/**
* Interface used by NavigationController to inform the client of changes. This largely duplicates
@@ -29,4 +30,7 @@ interface INavigationControllerClient {
void loadProgressChanged(double progress) = 7;
void onFirstContentfulPaint() = 8;
+
+ // Added in M85.
+ void onOldPageNoLongerRendered(in String uri) = 9;
}
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
index 75966ad04a4..6ec60700f85 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl
@@ -30,4 +30,10 @@ interface IProfile {
// Added in Version 84.
void setBooleanSetting(int type, boolean value) = 7;
boolean getBooleanSetting(int type) = 8;
+
+ // Added in Version 85.
+ void getBrowserPersistenceIds(in IObjectWrapper resultCallback) = 9;
+ void removeBrowserPersistenceStorage(in String[] ids,
+ in IObjectWrapper resultCallback) = 10;
+ void prepareForPossibleCrossOriginNavigation() = 11;
}
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
index b396292d4da..c029b9a6c18 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
@@ -4,6 +4,8 @@
package org.chromium.weblayer_private.interfaces;
+import java.util.List;
+
import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient;
@@ -13,6 +15,7 @@ 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;
+import org.chromium.weblayer_private.interfaces.IWebMessageCallbackClient;
interface ITab {
void setClient(in ITabClient client) = 0;
@@ -51,4 +54,16 @@ interface ITab {
// Added in 84
void captureScreenShot(in float scale, in IObjectWrapper resultCallback) = 16;
+
+ // Added in 85
+ boolean setData(in Map data) = 17;
+
+ // Added in 85
+ Map getData() = 18;
+ void registerWebMessageCallback(in String jsObjectName,
+ in List<String> allowedOrigins,
+ in IWebMessageCallbackClient client) = 19;
+ void unregisterWebMessageCallback(in String jsObjectName) = 20;
+ boolean canTranslate() = 21;
+ void showTranslateUi() = 22;
}
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
index 7313c2c8cec..12b6c4cfeda 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
@@ -36,4 +36,11 @@ interface ITabClient {
// Added in M84.
void onTabDestroyed() = 8;
+
+ // Added in M85.
+ void onBackgroundColorChanged(in int color) = 9;
+
+ // Added in M85
+ void onScrollNotification(
+ in int notificationType, in float currentScrollRatio) = 10;
}
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
index cdaa4ebb3e3..7c8af14c1f6 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl
@@ -96,4 +96,8 @@ interface IWebLayer {
ISiteSettingsFragment createSiteSettingsFragmentImpl(
in IRemoteFragmentClient remoteFragmentClient,
in IObjectWrapper fragmentArgs) = 16;
+
+ // Added in Version 85.
+ void onMediaSessionServiceStarted(in IObjectWrapper sessionService, in Intent intent) = 17;
+ void onMediaSessionServiceDestroyed() = 18;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl
index 9857b597c48..e152bef181b 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl
@@ -8,4 +8,6 @@ import android.content.Intent;
interface IWebLayerClient {
Intent createIntent() = 0;
+ Intent createMediaSessionServiceIntent() = 1;
+ int getMediaSessionNotificationId() = 2;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageCallbackClient.aidl
new file mode 100644
index 00000000000..91ce8b87153
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageCallbackClient.aidl
@@ -0,0 +1,16 @@
+// Copyright 2020 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.IWebMessageReplyProxy;
+
+interface IWebMessageCallbackClient {
+ void onNewReplyProxy(in IWebMessageReplyProxy proxy,
+ in int proxyId,
+ in boolean isMainFrame,
+ in String sourceOrigin) = 0;
+ void onPostMessage(in int proxyId, in String message) = 1;
+ void onReplyProxyDestroyed(in int proxyId) = 2;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageReplyProxy.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageReplyProxy.aidl
new file mode 100644
index 00000000000..208c78d53ef
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebMessageReplyProxy.aidl
@@ -0,0 +1,9 @@
+// Copyright 2020 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 IWebMessageReplyProxy {
+ void postMessage(in String message) = 0;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ScrollNotificationType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ScrollNotificationType.java
new file mode 100644
index 00000000000..424442ffed3
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ScrollNotificationType.java
@@ -0,0 +1,18 @@
+// Copyright 2020 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({ScrollNotificationType.DIRECTION_CHANGED_UP,
+ ScrollNotificationType.DIRECTION_CHANGED_DOWN})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ScrollNotificationType {
+ int DIRECTION_CHANGED_UP = 0;
+ int DIRECTION_CHANGED_DOWN = 1;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java
index b6fd35e49e5..1b37b08d9b3 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java
@@ -9,8 +9,13 @@ import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-@IntDef({SettingType.BASIC_SAFE_BROWSING_ENABLED})
+@IntDef({SettingType.BASIC_SAFE_BROWSING_ENABLED, SettingType.UKM_ENABLED,
+ SettingType.EXTENDED_REPORTING_SAFE_BROWSING_ENABLED,
+ SettingType.REAL_TIME_SAFE_BROWSING_ENABLED})
@Retention(RetentionPolicy.SOURCE)
public @interface SettingType {
int BASIC_SAFE_BROWSING_ENABLED = 0;
+ int UKM_ENABLED = 1;
+ int EXTENDED_REPORTING_SAFE_BROWSING_ENABLED = 2;
+ int REAL_TIME_SAFE_BROWSING_ENABLED = 3;
}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java
new file mode 100644
index 00000000000..a76b13c3ba8
--- /dev/null
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java
@@ -0,0 +1,19 @@
+// Copyright 2020 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;
+
+/**
+ * Versioning related constants.
+ */
+public interface WebLayerVersionConstants {
+ /**
+ * Maximum allowed version skew. If the skew is greater than this, the implementation and client
+ * are not considered compatible, and WebLayer is unusable. The skew is the absolute value of
+ * the difference between the client major version and the implementation major version.
+ *
+ * @see WebLayer#isAvailable()
+ */
+ int MAX_SKEW = 4;
+}
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl
index 3664df20f44..ac1cd24f40f 100644
--- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl
+++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl
@@ -4,6 +4,9 @@
package org.chromium.weblayer_private.test_interfaces;
+import org.chromium.weblayer_private.interfaces.IObjectWrapper;
+import org.chromium.weblayer_private.interfaces.ITab;
+
interface ITestWebLayer {
// Force network connectivity state.
boolean isNetworkChangeAutoDetectOn() = 1;
@@ -19,4 +22,26 @@ interface ITestWebLayer {
// Forces the system location setting to enabled.
void setSystemLocationSettingEnabled(boolean enabled) = 6;
+
+ // See comments in TestWebLayer for details.
+ void waitForBrowserControlsMetadataState(in ITab tab,
+ in int top,
+ in int bottom,
+ in IObjectWrapper runnable) = 7;
+
+ void setAccessibilityEnabled(in boolean enabled) = 8;
+
+ boolean canBrowserControlsScroll(in ITab tab) = 9;
+
+ // Creates and shows a test infobar in |tab|, calling |runnable| when the addition (including
+ // animations) is complete.
+ void addInfoBar(in ITab tab, in IObjectWrapper runnable) = 10;
+
+ // Gets the infobar container view associated with |tab|.
+ IObjectWrapper getInfoBarContainerView(in ITab tab) = 11;
+
+ void setIgnoreMissingKeyForTranslateManager(in boolean ignore) = 12;
+ void forceNetworkConnectivityState(in boolean networkAvailable) = 13;
+
+ boolean canInfoBarContainerScroll(in ITab tab) = 14;
}
diff --git a/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml b/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml
new file mode 100644
index 00000000000..e119c1148fa
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item>
+ <bitmap
+ android:src="@drawable/infobar_shadow_top"
+ android:gravity="top|fill_horizontal"
+ android:tileMode="disabled" />
+ </item>
+ <item
+ android:top="@dimen/infobar_shadow_height"
+ android:drawable="@color/infobar_background_color" />
+</layer-list>
diff --git a/chromium/weblayer/browser/java/res/drawable/weblayer_tab_indicator.xml b/chromium/weblayer/browser/java/res/drawable/weblayer_tab_indicator.xml
new file mode 100644
index 00000000000..c8ab7e06481
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/drawable/weblayer_tab_indicator.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:left="@dimen/weblayer_tab_indicator_padding"
+ android:right="@dimen/weblayer_tab_indicator_padding" >
+ <shape android:shape="rectangle" >
+ <corners
+ android:topRightRadius="@dimen/weblayer_tab_indicator_radius"
+ android:topLeftRadius="@dimen/weblayer_tab_indicator_radius" />
+ </shape>
+ </item>
+</layer-list>
diff --git a/chromium/weblayer/browser/java/res/layout/site_settings_layout.xml b/chromium/weblayer/browser/java/res/layout/site_settings_layout.xml
index 7bb3c0b82f1..c9cf58e79f1 100644
--- a/chromium/weblayer/browser/java/res/layout/site_settings_layout.xml
+++ b/chromium/weblayer/browser/java/res/layout/site_settings_layout.xml
@@ -3,9 +3,14 @@
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
-<FrameLayout
+<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/site_settings_container">
-</FrameLayout>
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/site_settings_container">
+ </FrameLayout>
+ <include layout="@layout/settings_action_bar_shadow" />
+</RelativeLayout>
diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml
new file mode 100644
index 00000000000..82149b5b142
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/weblayer_translate_infobar_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <!-- TODO(huayinz): Change app:tabIndicatorColor to some common color reference -->
+ <org.chromium.weblayer_private.TranslateTabLayout
+ android:id="@+id/weblayer_translate_infobar_tabs"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:requiresFadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/weblayer_infobar_translate_fade_edge_length"
+ app:tabIndicator="@drawable/weblayer_tab_indicator"
+ app:tabIndicatorFullWidth="false"
+ app:tabIndicatorHeight="3dp"
+ app:tabSelectedTextColor="@color/weblayer_tab_layout_selected_tab_color"
+ app:tabGravity="fill"
+ app:tabMode="scrollable" />
+
+ <org.chromium.ui.widget.ChromeImageButton
+ android:id="@+id/weblayer_translate_infobar_menu_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minHeight="@dimen/min_touch_target_size"
+ android:minWidth="@dimen/min_touch_target_size"
+ android:scaleType="center"
+ android:background="?attr/selectableItemBackground"
+ android:contentDescription="@string/accessibility_toolbar_btn_menu"
+ android:src="@drawable/ic_more_vert_24dp"
+ app:tint="@color/default_icon_color_tint_list" />
+</LinearLayout>
diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml
new file mode 100644
index 00000000000..3be8f467c43
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+
+<org.chromium.weblayer_private.TranslateTabContent
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/weblayer_translate_tabcontent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <!-- Add both the textView and progressBar to the tab, and only keep one of them visible.
+ This way the width of the Tab will always be fixed no matter which one is visible. -->
+ <TextView
+ android:id="@+id/weblayer_translate_infobar_tab_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textAppearance="@style/TextAppearance.Design.Tab"
+ android:visibility="visible"
+ android:singleLine="true" />
+ <ProgressBar
+ android:id="@+id/weblayer_translate_infobar_tab_progressbar"
+ android:layout_width="@dimen/infobar_small_icon_size"
+ android:layout_height="@dimen/infobar_small_icon_size"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ android:visibility="invisible" />
+</org.chromium.weblayer_private.TranslateTabContent>
diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml
new file mode 100644
index 00000000000..a7948450da3
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/WebLayerAppMenuItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/weblayer_menu_item_text"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:gravity="center_vertical"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp" />
+
+</FrameLayout>
diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml
new file mode 100644
index 00000000000..a4bd2cf4692
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ style="@style/WebLayerAppMenuItem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <TextView
+ android:id="@+id/weblayer_menu_item_text"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="start"
+ android:gravity="center_vertical"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp"
+ android:paddingEnd="16dp" />
+ <org.chromium.ui.widget.ChromeImageView
+ android:id="@+id/weblayer_menu_item_icon"
+ android:src="@drawable/ic_check_googblue_24dp"
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:gravity="center_vertical"
+ app:tint="@color/default_icon_color_tint_list" />
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/weblayer_menu_item_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/divider_line_bg_color"
+ android:visibility="gone" />
+
+</LinearLayout>
+
diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_url_bar.xml b/chromium/weblayer/browser/java/res/layout/weblayer_url_bar.xml
index 1e8828d7f25..e5f2b57b80e 100644
--- a/chromium/weblayer/browser/java/res/layout/weblayer_url_bar.xml
+++ b/chromium/weblayer/browser/java/res/layout/weblayer_url_bar.xml
@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:maxLines="1"
+ android:singleLine="true"
android:paddingEnd="@dimen/url_text_edge_padding"
android:paddingStart="@dimen/url_text_edge_padding"
android:ellipsize="start"
diff --git a/chromium/weblayer/browser/java/res/values/colors.xml b/chromium/weblayer/browser/java/res/values/colors.xml
new file mode 100644
index 00000000000..bb00b313960
--- /dev/null
+++ b/chromium/weblayer/browser/java/res/values/colors.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Please see src/ui/android/java/res/values/colors.xml for the shared common colors. -->
+
+ <color name="weblayer_tab_layout_selected_tab_color">@color/default_text_color_blue</color>
+
+</resources>
diff --git a/chromium/weblayer/browser/java/res/values/dimens.xml b/chromium/weblayer/browser/java/res/values/dimens.xml
index dfce8d8e51e..0eee3fc9758 100644
--- a/chromium/weblayer/browser/java/res/values/dimens.xml
+++ b/chromium/weblayer/browser/java/res/values/dimens.xml
@@ -6,4 +6,13 @@
<resources>
<dimen name="security_status_icon_size">18dp</dimen>
<dimen name="url_text_edge_padding">5dp</dimen>
-</resources> \ No newline at end of file
+
+ <!-- Dimensions for compact translate infobar. -->
+ <dimen name="weblayer_infobar_translate_fade_edge_length">18dp</dimen>
+ <dimen name="weblayer_infobar_translate_menu_width">260dp</dimen>
+
+ <!-- Dimens of tab indicator -->
+ <dimen name="weblayer_tab_indicator_radius">3dp</dimen>
+ <dimen name="weblayer_tab_indicator_padding">2dp</dimen>
+
+</resources>
diff --git a/chromium/weblayer/browser/java/res/values/styles.xml b/chromium/weblayer/browser/java/res/values/styles.xml
index b90beb2e45c..f362fd9ab69 100644
--- a/chromium/weblayer/browser/java/res/values/styles.xml
+++ b/chromium/weblayer/browser/java/res/values/styles.xml
@@ -10,40 +10,13 @@
<item name="alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
</style>
- <style name="PreferenceTheme">
- <item name="preferenceStyle">@style/PreferenceItem</item>
- <item name="preferenceFragmentCompatStyle">@style/SettingsFragment</item>
- <item name="preferenceFragmentListStyle">@style/SettingsFragmentList</item>
- <item name="dialogPreferenceStyle">@style/DialogPreference</item>
- <item name="checkBoxPreferenceStyle">@style/CheckBoxPreference</item>
- <item name="switchPreferenceCompatStyle">@style/SwitchPreference</item>
- </style>
-
- <style name="PreferenceItem">
- <item name="android:layout">@layout/preference_compat</item>
- </style>
-
- <style name="SettingsFragment">
- <item name="android:divider">?android:attr/listDivider</item>
- </style>
-
- <style name="SettingsFragmentList">
- <item name="android:paddingStart">0dp</item>
- <item name="android:paddingEnd">0dp</item>
- </style>
-
- <style name="DialogPreference">
- <item name="android:layout">@layout/preference_compat</item>
- <item name="android:negativeButtonText">@android:string/cancel</item>
- </style>
-
- <style name="CheckBoxPreference">
- <item name="android:layout">@layout/preference_compat</item>
- <item name="android:widgetLayout">@layout/preference_widget_checkbox</item>
- </style>
-
- <style name="SwitchPreference">
- <item name="android:layout">@layout/preference_compat</item>
- <item name="android:widgetLayout">@layout/preference_widget_switch_compat</item>
+ <!-- The following styles may be used to style views provided by a CustomViewBinder or attached
+ to the app menu as headers or footers. -->
+
+ <!-- Styling for an app menu item row. -->
+ <style name="WebLayerAppMenuItem">
+ <item name="android:paddingStart">16dp</item>
+ <item name="android:paddingEnd">16dp</item>
+ <item name="android:background">?attr/listChoiceBackgroundIndicator</item>
</style>
</resources>
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb
index 403610204ab..62d289ff62c 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="af">
+<translation id="1068672505746868501">Moet nooit bladsye in <ph name="SOURCE_LANGUAGE" /> vertaal nie</translation>
+<translation id="124116460088058876">Meer tale</translation>
+<translation id="1285320974508926690">Moet nooit hierdie werf vertaal nie</translation>
+<translation id="290376772003165898">Bladsy is nie in <ph name="LANGUAGE" /> nie?</translation>
+<translation id="5684874026226664614">Oeps. Hierdie bladsy kon nie vertaal word nie.</translation>
+<translation id="6040143037577758943">Maak toe</translation>
+<translation id="6831043979455480757">Vertaal</translation>
+<translation id="7243308994586599757">Opsies is naby die onderkant van die skerm beskikbaar</translation>
+<translation id="773466115871691567">Vertaal altyd bladsye in <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Webblaaieraktiwiteit</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb
index 2fd6e46a16e..ec9bad225cb 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="am">
+<translation id="1068672505746868501">በ<ph name="SOURCE_LANGUAGE" /> ውስጥ ገጾችን በጭራሽ አትተርጉም</translation>
+<translation id="124116460088058876">ተጨማሪ ቋንቋዎች</translation>
+<translation id="1285320974508926690">ይህን ጣቢያ በጭራሽ አትተርጉም</translation>
+<translation id="290376772003165898">ገጽ በ<ph name="LANGUAGE" /> አይደለም?</translation>
+<translation id="5684874026226664614">ውይ። ይህ ገጽ ሊተረጎም አይችልም።</translation>
+<translation id="6040143037577758943">ዝጋ</translation>
+<translation id="6831043979455480757">መተርጎም</translation>
+<translation id="7243308994586599757">አማራጮች ከማያ ገጹ ግርጌ አጠገብ ይገኛሉ</translation>
+<translation id="773466115871691567">ገጾችን ሁልጊዜ በ<ph name="SOURCE_LANGUAGE" /> ተርጉም</translation>
<translation id="8298278839890148234">የድር አሳሽ እንቅስቃሴ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb
index 55fcefece9b..b631d5aa80b 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ar">
+<translation id="1068672505746868501">عدم ترجمة الصفحات باللغة <ph name="SOURCE_LANGUAGE" /> مُطلقًا</translation>
+<translation id="124116460088058876">مزيد من اللغات</translation>
+<translation id="1285320974508926690">عدم ترجمة هذا الموقع مطلقًا</translation>
+<translation id="290376772003165898">أليست الصفحة باللغة <ph name="LANGUAGE" />؟</translation>
+<translation id="5684874026226664614">عفوًا. تعذرت ترجمة هذه الصفحة.</translation>
+<translation id="6040143037577758943">إغلاق</translation>
+<translation id="6831043979455480757">ترجمة</translation>
+<translation id="7243308994586599757">الخيارات المتاحة بالقرب من الجزء السفلي من الشاشة</translation>
+<translation id="773466115871691567">ترجمة الصفحات باللغة <ph name="SOURCE_LANGUAGE" /> دائمًا</translation>
<translation id="8298278839890148234">نشاط التصفُّح على الويب</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb
index db63c417200..409550b1d05 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="as">
+<translation id="1068672505746868501">পৃষ্ঠা <ph name="SOURCE_LANGUAGE" />লৈ কেতিয়াও অনুবাদ নকৰিব</translation>
+<translation id="124116460088058876">অধিক ভাষা</translation>
+<translation id="1285320974508926690">এই ছাইটটো কেতিয়াও অনুবাদ নকৰিব</translation>
+<translation id="290376772003165898">পৃষ্ঠাটো <ph name="LANGUAGE" /> ভাষাত নাই নেকি?</translation>
+<translation id="5684874026226664614">ওঁহ এই পৃষ্ঠাটো অনুবাদ কৰিব পৰা নগ’ল।</translation>
+<translation id="6040143037577758943">বন্ধ কৰক</translation>
+<translation id="6831043979455480757">অনুবাদ কৰক</translation>
+<translation id="7243308994586599757">স্ক্ৰীণৰ কাষৰ বুটামত উপলব্ধ বিকল্প</translation>
+<translation id="773466115871691567">সদায় পৃষ্ঠাসমূহ <ph name="SOURCE_LANGUAGE" />লৈ অনুবাদ কৰক</translation>
<translation id="8298278839890148234">ৱেব ব্ৰাউজাৰৰ কার্যকলাপ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb
index 684871cc3c3..cd8a4a44f50 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="az">
+<translation id="1068672505746868501">Heç vaxt <ph name="SOURCE_LANGUAGE" /> dilində olan səhifələri tərcümə etməyin</translation>
+<translation id="124116460088058876">Digər dillər</translation>
+<translation id="1285320974508926690">Bu saytı heç vaxt tərcümə etməyin</translation>
+<translation id="290376772003165898">Səhifə <ph name="LANGUAGE" /> dilində deyil?</translation>
+<translation id="5684874026226664614">Bu səhifə tərcümə edilə bilmir. Niyəsini bilmirik.</translation>
+<translation id="6040143037577758943">Qapat</translation>
+<translation id="6831043979455480757">Tərcümə et</translation>
+<translation id="7243308994586599757">Seçənəklər ekranın aşağısına yaxın yerdə əlçatandır</translation>
+<translation id="773466115871691567">Səhifələri həmişə <ph name="SOURCE_LANGUAGE" /> dilinə tərcümə edin</translation>
<translation id="8298278839890148234">Veb axtarış fəaliyyəti</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb
index 4c18cf28a2f..eff070e867a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="be">
+<translation id="1068672505746868501">Ніколі не перакладаць старонкі на наступнай мове: <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Іншыя мовы</translation>
+<translation id="1285320974508926690">Ніколі не перакладаць гэты сайт</translation>
+<translation id="290376772003165898">Мова старонкі – не <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Памылка. Не ўдалося перакласці гэту старонку.</translation>
+<translation id="6040143037577758943">Закрыць</translation>
+<translation id="6831043979455480757">Перакласці</translation>
+<translation id="7243308994586599757">Параметры знаходзяцца ў ніжняй частцы экрана</translation>
+<translation id="773466115871691567">Заўсёды перакладаць старонкі на наступнай мове: <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Дзеянні ў вэб-браўзеры</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb
index 1e7789939c0..a55fe167dff 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="bg">
+<translation id="1068672505746868501">Страниците на <ph name="SOURCE_LANGUAGE" /> да не се превеждат</translation>
+<translation id="124116460088058876">Още езици</translation>
+<translation id="1285320974508926690">Този сайт да не се превежда никога</translation>
+<translation id="290376772003165898">Страницата не е на <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ами сега! Тази страница не можа да се преведе.</translation>
+<translation id="6040143037577758943">Затваряне</translation>
+<translation id="6831043979455480757">Превод</translation>
+<translation id="7243308994586599757">Опциите са в долната част на екрана</translation>
+<translation id="773466115871691567">Страниците на <ph name="SOURCE_LANGUAGE" /> да се превеждат винаги</translation>
<translation id="8298278839890148234">Активност в уеб браузъра</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb
index 374b81798d7..81f76d848f2 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="bn">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ভাষার পৃষ্ঠার অনুবাদ কখনও দেখতে চাই না</translation>
+<translation id="124116460088058876">আরও ভাষা</translation>
+<translation id="1285320974508926690">কখনই এই সাইটটিকে অনুবাদ করবেন না</translation>
+<translation id="290376772003165898">পৃষ্ঠাটি <ph name="LANGUAGE" /> ভাষায় নয়?</translation>
+<translation id="5684874026226664614">ওহো৷ এই পৃষ্ঠাটির অনুবাদ করা যাবে না৷</translation>
+<translation id="6040143037577758943">বন্ধ</translation>
+<translation id="6831043979455480757">অনুবাদ</translation>
+<translation id="7243308994586599757">স্ক্রীনের প্রায় নীচের দিকে বিকল্পগুলি উপলব্ধ</translation>
+<translation id="773466115871691567">সব সময় <ph name="SOURCE_LANGUAGE" /> ভাষার পৃষ্ঠার অনুবাদ দেখতে চাই</translation>
<translation id="8298278839890148234">ওয়েব ব্রাউজার অ্যাক্টিভিটি</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb
index 4fb2dbb390e..5d1aefcaea1 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="bs">
+<translation id="1068672505746868501">Nemoj nikada prevoditi stranice na <ph name="SOURCE_LANGUAGE" /> jezik</translation>
+<translation id="124116460088058876">Više jezika</translation>
+<translation id="1285320974508926690">Nikada ne prevodi ovu web lokaciju</translation>
+<translation id="290376772003165898">Ovo nije <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups. Prijevod ove stranice nije uspio.</translation>
+<translation id="6040143037577758943">Zatvori</translation>
+<translation id="6831043979455480757">Prevedi</translation>
+<translation id="7243308994586599757">Opcije su dostupne pri dnu ekrana</translation>
+<translation id="773466115871691567">Uvijek prevedi stranice na <ph name="SOURCE_LANGUAGE" /> jezik</translation>
<translation id="8298278839890148234">Aktivnost web preglednika</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb
index ba2d4e4da24..3c181c2e235 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ca">
+<translation id="1068672505746868501">No tradueixis mai les pàgines en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Més idiomes</translation>
+<translation id="1285320974508926690">No tradueixis mai aquest lloc</translation>
+<translation id="290376772003165898">La pàgina no està en <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Aquesta pàgina no s'ha pogut traduir.</translation>
+<translation id="6040143037577758943">Tanca</translation>
+<translation id="6831043979455480757">Tradueix</translation>
+<translation id="7243308994586599757">Opcions disponibles a la part inferior de la pantalla</translation>
+<translation id="773466115871691567">Tradueix sempre les pàgines en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Activitat del navegador web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb
index 5710c42229a..7e7b91a9744 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="cs">
+<translation id="1068672505746868501">Stránky v jazyce <ph name="SOURCE_LANGUAGE" /> nikdy nepřekládat</translation>
+<translation id="124116460088058876">Další jazyky</translation>
+<translation id="1285320974508926690">Tento web nikdy nepřekládat</translation>
+<translation id="290376772003165898">Stránka není v jazyce <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Jejda. Tuto stránku se nepodařilo přeložit.</translation>
+<translation id="6040143037577758943">Zavřít</translation>
+<translation id="6831043979455480757">Přeložit</translation>
+<translation id="7243308994586599757">Možnosti jsou k dispozici ve spodní části obrazovky</translation>
+<translation id="773466115871691567">Stránky v jazyce <ph name="SOURCE_LANGUAGE" /> vždy překládat</translation>
<translation id="8298278839890148234">Aktivita ve webovém prohlížeči</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb
index 63df7f53aa1..571f36b83ac 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="da">
+<translation id="1068672505746868501">Oversæt aldrig sider på <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Flere sprog</translation>
+<translation id="1285320974508926690">Oversæt aldrig dette website</translation>
+<translation id="290376772003165898">Er siden ikke på <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups! Denne side kunne ikke oversættes.</translation>
+<translation id="6040143037577758943">Luk</translation>
+<translation id="6831043979455480757">Oversæt</translation>
+<translation id="7243308994586599757">Du finder indstillingerne nederst på skærmen</translation>
+<translation id="773466115871691567">Oversæt altid sider på <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Webbrowseraktivitet</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb
index a6c724e09b6..65cd87c466a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="de">
+<translation id="1068672505746868501">Seiten auf <ph name="SOURCE_LANGUAGE" /> nie übersetzen</translation>
+<translation id="124116460088058876">Weitere Sprachen</translation>
+<translation id="1285320974508926690">Diese Website nie übersetzen</translation>
+<translation id="290376772003165898">Diese Seite ist nicht auf <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Hoppla! Diese Seite konnte nicht übersetzt werden.</translation>
+<translation id="6040143037577758943">Schließen</translation>
+<translation id="6831043979455480757">Übersetzen</translation>
+<translation id="7243308994586599757">Optionen unten auf dem Bildschirm verfügbar</translation>
+<translation id="773466115871691567">Seiten auf <ph name="SOURCE_LANGUAGE" /> immer übersetzen</translation>
<translation id="8298278839890148234">Webbrowseraktivitäten</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb
index 91f7efb7d08..1e22b4a980c 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="el">
+<translation id="1068672505746868501">Να μην γίνεται μετάφραση σελίδων στα <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Περισσότερες γλώσσες</translation>
+<translation id="1285320974508926690">Να μην γίνεται ποτέ μετάφραση αυτού του ιστότοπου</translation>
+<translation id="290376772003165898">Η σελίδα δεν είναι στα <ph name="LANGUAGE" />;</translation>
+<translation id="5684874026226664614">Ωχ. Δεν ήταν δυνατή η μετάφραση αυτής της σελίδας.</translation>
+<translation id="6040143037577758943">Κλείσιμο</translation>
+<translation id="6831043979455480757">Μετάφραση</translation>
+<translation id="7243308994586599757">Διαθέσιμες επιλογές κοντά κάτω μέρος της οθόνης</translation>
+<translation id="773466115871691567">Να μεταφράζονται πάντα οι σελίδες προς τα <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Δραστηριότητα προγράμματος περιήγησης στον ιστό</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb
index 078cecbe647..8f63e62c0db 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="en-GB">
+<translation id="1068672505746868501">Never translate pages in <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">More languages</translation>
+<translation id="1285320974508926690">Never translate this site</translation>
+<translation id="290376772003165898">Page is not in <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Oops. This page could not be translated.</translation>
+<translation id="6040143037577758943">Close</translation>
+<translation id="6831043979455480757">Translate</translation>
+<translation id="7243308994586599757">Options available near bottom of the screen</translation>
+<translation id="773466115871691567">Always translate pages in <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Web browser activity</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb
index e529fa34f49..276fe173c58 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="es-419">
+<translation id="1068672505746868501">No traducir nunca páginas en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Más idiomas</translation>
+<translation id="1285320974508926690">Nunca traducir este sitio</translation>
+<translation id="290376772003165898">¿La página no está en <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">No se puede traducir esta página.</translation>
+<translation id="6040143037577758943">Cerrar</translation>
+<translation id="6831043979455480757">Traducir</translation>
+<translation id="7243308994586599757">Opciones disponibles junto a la parte inferior de la pantalla</translation>
+<translation id="773466115871691567">Traducir siempre las páginas en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Actividad del navegador web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb
index fcf6da55978..8b61d2a834e 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="es">
+<translation id="1068672505746868501">No traducir nunca las páginas en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Más idiomas</translation>
+<translation id="1285320974508926690">No traducir nunca este sitio</translation>
+<translation id="290376772003165898">¿La página no está en <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">¡Vaya! No se ha podido traducir esta página.</translation>
+<translation id="6040143037577758943">Cerrar</translation>
+<translation id="6831043979455480757">Traducir</translation>
+<translation id="7243308994586599757">Opciones disponibles cerca de la parte inferior de la pantalla</translation>
+<translation id="773466115871691567">Traducir siempre las páginas en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Actividad del navegador web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb
index 37ada0e9df4..ff14c3ea03e 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="et">
+<translation id="1068672505746868501">Ära tõlgi kunagi <ph name="SOURCE_LANGUAGE" /> keeles olevaid lehti</translation>
+<translation id="124116460088058876">Rohkem keeli</translation>
+<translation id="1285320974508926690">Ära kunagi seda saiti tõlgi</translation>
+<translation id="290376772003165898">Kas leht ei ole <ph name="LANGUAGE" /> keeles?</translation>
+<translation id="5684874026226664614">Vabandust. Lehte ei õnnestunud tõlkida.</translation>
+<translation id="6040143037577758943">Sulge</translation>
+<translation id="6831043979455480757">Tõlgi</translation>
+<translation id="7243308994586599757">Valikud on saadaval ekraani allosas</translation>
+<translation id="773466115871691567">Tõlgi alati <ph name="SOURCE_LANGUAGE" /> keeles olevad lehed</translation>
<translation id="8298278839890148234">Veebibrauseri tegevused</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb
index 2698f9fb351..0fe579c1cb0 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="eu">
+<translation id="1068672505746868501">Ez itzuli inoiz <ph name="SOURCE_LANGUAGE" /> darabilten orriak</translation>
+<translation id="124116460088058876">Hizkuntza gehiago</translation>
+<translation id="1285320974508926690">Ez itzuli inoiz webgune hau</translation>
+<translation id="290376772003165898">Ez al da <ph name="LANGUAGE" /> orriko hizkuntza?</translation>
+<translation id="5684874026226664614">Ezin izan da orria itzuli.</translation>
+<translation id="6040143037577758943">Itxi</translation>
+<translation id="6831043979455480757">Itzuli</translation>
+<translation id="7243308994586599757">Pantailaren behealdean agertzen dira dauden aukerak</translation>
+<translation id="773466115871691567">Itzuli beti <ph name="SOURCE_LANGUAGE" /> darabilten orriak</translation>
<translation id="8298278839890148234">Sareko arakatze-jarduerak</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb
index 77daea83d86..b3cf59018eb 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fa">
+<translation id="1068672505746868501">هرگز صفحه‌های <ph name="SOURCE_LANGUAGE" /> ترجمه نشوند</translation>
+<translation id="124116460088058876">زبان‌های بیشتر</translation>
+<translation id="1285320974508926690">این سایت هرگز ترجمه نشود</translation>
+<translation id="290376772003165898">صفحه به زبان <ph name="LANGUAGE" /> وجود ندارد؟</translation>
+<translation id="5684874026226664614">متأسفیم. این صفحه ترجمه نشد.</translation>
+<translation id="6040143037577758943">بستن</translation>
+<translation id="6831043979455480757">ترجمه</translation>
+<translation id="7243308994586599757">گزینه‌ها در نزدیک پایین صفحه نمایش در دسترس هستند</translation>
+<translation id="773466115871691567">صفحه‌های <ph name="SOURCE_LANGUAGE" /> همیشه ترجمه شوند</translation>
<translation id="8298278839890148234">فعالیت مرورگر وب</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb
index 78a8d793d2e..e8e035371e0 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fi">
+<translation id="1068672505746868501">Älä koskaan käännä kielellä <ph name="SOURCE_LANGUAGE" /> kirjoitettuja sivuja.</translation>
+<translation id="124116460088058876">Lisää kieliä</translation>
+<translation id="1285320974508926690">Älä käännä tätä sivustoa</translation>
+<translation id="290376772003165898">Eikö sivu ole kirjoitettu kielellä <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Hups, tätä sivua ei voi kääntää.</translation>
+<translation id="6040143037577758943">Sulje</translation>
+<translation id="6831043979455480757">Käännä</translation>
+<translation id="7243308994586599757">Asetukset löytyvät näytön alalaidasta.</translation>
+<translation id="773466115871691567">Käännä aina kielellä <ph name="SOURCE_LANGUAGE" /> kirjoitut sivut</translation>
<translation id="8298278839890148234">Verkon selaustoiminta</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb
index 08f97ffffd4..01ddf5eb7ec 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fil">
+<translation id="1068672505746868501">Huwag kailanman isalin ang mga page sa <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Higit pang wika</translation>
+<translation id="1285320974508926690">Huwag isalin kailanman ang site na ito</translation>
+<translation id="290376772003165898">Hindi nakasalin ang page sa <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Oops. Hindi maisalin ang pahinang ito.</translation>
+<translation id="6040143037577758943">Isara</translation>
+<translation id="6831043979455480757">Isalin</translation>
+<translation id="7243308994586599757">May mga opsyon malapit sa ibaba ng screen</translation>
+<translation id="773466115871691567">Palaging isalin ang mga page sa <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktibidad ng web browser</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb
index 537978cc551..2415250b42a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fr-CA">
+<translation id="1068672505746868501">Ne jamais traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Plus de langues</translation>
+<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
+<translation id="290376772003165898">Cette page n'est pas en <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Oups… Impossible de traduire cette page.</translation>
+<translation id="6040143037577758943">Fermer</translation>
+<translation id="6831043979455480757">Traduire</translation>
+<translation id="7243308994586599757">Options disponibles vers le bas de l’écran</translation>
+<translation id="773466115871691567">Toujours traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Activité de navigation Web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb
index 9810a3e323a..334bf0344fd 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="fr">
+<translation id="1068672505746868501">Ne jamais traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Plus de langues</translation>
+<translation id="1285320974508926690">Ne jamais traduire ce site</translation>
+<translation id="290376772003165898">La page n'est pas en <ph name="LANGUAGE" /> ?</translation>
+<translation id="5684874026226664614">Petit problème… Impossible de traduire cette page.</translation>
+<translation id="6040143037577758943">Fermer</translation>
+<translation id="6831043979455480757">Traduire</translation>
+<translation id="7243308994586599757">Options disponibles au bas de l'écran</translation>
+<translation id="773466115871691567">Toujours traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Activité de navigation sur le Web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb
index c315bcbcd56..3f598e52c3b 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="gl">
+<translation id="1068672505746868501">Non traducir nunca páxinas en <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Máis idiomas</translation>
+<translation id="1285320974508926690">Non traducir nunca este sitio</translation>
+<translation id="290376772003165898">A páxina non está en <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Non se puido traducir esta páxina.</translation>
+<translation id="6040143037577758943">Pechar</translation>
+<translation id="6831043979455480757">Traducir</translation>
+<translation id="7243308994586599757">Opcións dispoñibles na parte inferior da pantalla</translation>
+<translation id="773466115871691567">Traducir sempre páxinas en <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Actividade de navegación pola Web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb
index 9e2ee22696d..957cc2993a6 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="gu">
+<translation id="1068672505746868501">પેજનો ક્યારેય પણ <ph name="SOURCE_LANGUAGE" />માં અનુવાદ કરશો નહીં</translation>
+<translation id="124116460088058876">વધુ ભાષાઓ</translation>
+<translation id="1285320974508926690">આ સાઇટનું ક્યારેય ભાષાંતર કરશો નહીં</translation>
+<translation id="290376772003165898">પેજ <ph name="LANGUAGE" />માં નથી?</translation>
+<translation id="5684874026226664614">અરેરે. આ પૃષ્ઠનો અનુવાદ કરી શકાયો નથી.</translation>
+<translation id="6040143037577758943">બંધ કરો</translation>
+<translation id="6831043979455480757">અનુવાદ કરો</translation>
+<translation id="7243308994586599757">સ્ક્રીનના તળિયા નજીક વિકલ્પો ઉપલબ્ધ છે</translation>
+<translation id="773466115871691567">હંમેશાં પેજનો <ph name="SOURCE_LANGUAGE" />માં અનુવાદ કરો</translation>
<translation id="8298278839890148234">વેબ બ્રાઉઝરની પ્રવૃત્તિ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb
index a9ae0e70db1..6fe2057217c 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="hi">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> भाषा के पेज का कभी भी अनुवाद न करें</translation>
+<translation id="124116460088058876">ज़्यादा भाषाएं</translation>
+<translation id="1285320974508926690">कभी भी इस साइट का अनुवाद न करें</translation>
+<translation id="290376772003165898">क्या पेज <ph name="LANGUAGE" /> भाषा में नहीं है?</translation>
+<translation id="5684874026226664614">ओह. इस पेज का अनुवाद नहीं किया जा सका.</translation>
+<translation id="6040143037577758943">बंद करें</translation>
+<translation id="6831043979455480757">अनुवाद करें</translation>
+<translation id="7243308994586599757">विकल्‍प स्‍क्रीन के नीचे उपलब्‍ध हैं</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> भाषा के पेज का हमेशा अनुवाद करें</translation>
<translation id="8298278839890148234">वेब ब्राउज़र की गतिविधि</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb
index bc0817b9c82..d9fc4ee6cec 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="hr">
+<translation id="1068672505746868501">Nikad ne prevodi <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Više jezika</translation>
+<translation id="1285320974508926690">Nikad nemoj prevoditi ovu web-lokaciju</translation>
+<translation id="290376772003165898">Ovo nije <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups. Stranicu nije bilo moguće prevesti.</translation>
+<translation id="6040143037577758943">Zatvori</translation>
+<translation id="6831043979455480757">Prevedi</translation>
+<translation id="7243308994586599757">Opcije dostupne pri dnu zaslona</translation>
+<translation id="773466115871691567">Uvijek prevodi <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktivnost u web-pregledniku</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb
index 587b17fb1f5..498d74cc4c9 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="hu">
+<translation id="1068672505746868501">Soha ne fordítsa le a(z) <ph name="SOURCE_LANGUAGE" /> nyelvű oldalakat</translation>
+<translation id="124116460088058876">További nyelvek…</translation>
+<translation id="1285320974508926690">Ezt a webhelyet soha ne fordítsa le</translation>
+<translation id="290376772003165898">Az oldal nem <ph name="LANGUAGE" /> nyelvű?</translation>
+<translation id="5684874026226664614">Hoppá! Az oldalt nem sikerült lefordítani.</translation>
+<translation id="6040143037577758943">Bezárás</translation>
+<translation id="6831043979455480757">Fordítás</translation>
+<translation id="7243308994586599757">A beállítások a képernyő alsó részén találhatók</translation>
+<translation id="773466115871691567">Mindig fordítsa le a(z) <ph name="SOURCE_LANGUAGE" /> nyelvű oldalakat</translation>
<translation id="8298278839890148234">Böngészős tevékenységek</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb
index c5676da7cb3..e3c1efcec5e 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="hy">
+<translation id="1068672505746868501">Երբեք չթարգմանել <ph name="SOURCE_LANGUAGE" /> էջերը</translation>
+<translation id="124116460088058876">Այլ լեզուներ</translation>
+<translation id="1285320974508926690">Երբեք չթարգմանել այս կայքը</translation>
+<translation id="290376772003165898">Էջը <ph name="LANGUAGE" /> չէ՞</translation>
+<translation id="5684874026226664614">Չհաջողվեց թարգմանել այս էջը:</translation>
+<translation id="6040143037577758943">Փակել</translation>
+<translation id="6831043979455480757">Թարգմանել</translation>
+<translation id="7243308994586599757">Ընտրանքները հասանելի են էկրանի ստորին հատվածում</translation>
+<translation id="773466115871691567">Միշտ թարգմանել <ph name="SOURCE_LANGUAGE" /> էջերը</translation>
<translation id="8298278839890148234">Գործողություններ դիտարկիչում</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb
index 31b5fe5d797..673f32be14c 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="id">
+<translation id="1068672505746868501">Jangan pernah menerjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Bahasa lainnya</translation>
+<translation id="1285320974508926690">Jangan pernah terjemahkan situs ini</translation>
+<translation id="290376772003165898">Halaman tidak dalam bahasa <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups. Halaman ini tidak dapat diterjemahkan.</translation>
+<translation id="6040143037577758943">Tutup</translation>
+<translation id="6831043979455480757">Terjemahkan</translation>
+<translation id="7243308994586599757">Opsi terdapat di dekat bagian bawah layar</translation>
+<translation id="773466115871691567">Selalu terjemahkan halaman dalam bahasa <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktivitas penjelajahan web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb
index 923602bb298..2f77ab69860 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="is">
+<translation id="1068672505746868501">Þýða aldrei síður á þessu tungumáli: <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Fleiri tungumál</translation>
+<translation id="1285320974508926690">Aldrei þýða þetta vefsvæði</translation>
+<translation id="290376772003165898">Er tungumál síðunnar ekki <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Úbbs. Þessa síðu er ekki hægt að þýða.</translation>
+<translation id="6040143037577758943">Loka</translation>
+<translation id="6831043979455480757">Þýða</translation>
+<translation id="7243308994586599757">Valkostir eru neðst á skjánum</translation>
+<translation id="773466115871691567">Þýða alltaf síður á þessu tungumáli: <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Vafranotkun</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb
index c4dc6e6ca5d..1f19226503d 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="it">
+<translation id="1068672505746868501">Non tradurre mai le pagine in <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Altre lingue</translation>
+<translation id="1285320974508926690">Non tradurre mai questo sito</translation>
+<translation id="290376772003165898">La pagina non è in <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Spiacenti. Impossibile tradurre questa pagina.</translation>
+<translation id="6040143037577758943">Chiudi</translation>
+<translation id="6831043979455480757">Traduci</translation>
+<translation id="7243308994586599757">Opzioni disponibili nella parte inferiore dello schermo</translation>
+<translation id="773466115871691567">Traduci sempre le pagine in <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Attività del browser web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb
index bea498feca7..61e44631e2e 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="iw">
+<translation id="1068672505746868501">אף פעם אל תתרגם דפים ב<ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">שפות נוספות</translation>
+<translation id="1285320974508926690">איני רוצה לקבל תרגום של אתר זה</translation>
+<translation id="290376772003165898">הדף לא ב<ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">אופס. לא ניתן היה לתרגם את הדף הזה.</translation>
+<translation id="6040143037577758943">סגור</translation>
+<translation id="6831043979455480757">תרגום</translation>
+<translation id="7243308994586599757">אפשרויות הזמינות באזור החלק התחתון של המסך</translation>
+<translation id="773466115871691567">ברצוני לקבל תרגום תמיד דפים ב<ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">פעילות דפדפן אינטרנט</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb
index 091fdcc87ff..cf3a9d91955 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ja">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />のページを翻訳しない</translation>
+<translation id="124116460088058876">その他の言語</translation>
+<translation id="1285320974508926690">このサイトは翻訳しない</translation>
+<translation id="290376772003165898"><ph name="LANGUAGE" />のページではない場合</translation>
+<translation id="5684874026226664614">このページを翻訳できませんでした。</translation>
+<translation id="6040143037577758943">閉じる</translation>
+<translation id="6831043979455480757">翻訳</translation>
+<translation id="7243308994586599757">画面の下の方にオプションがあります</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />のページを常に翻訳する</translation>
<translation id="8298278839890148234">ウェブブラウザのアクティビティ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb
index 57bc785af7f..2aa6a13f2ff 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ka">
+<translation id="1068672505746868501">არასოდეს ითარგმნოს <ph name="SOURCE_LANGUAGE" /> გვერდები</translation>
+<translation id="124116460088058876">სხვა ენები</translation>
+<translation id="1285320974508926690">არასდროს გადათარგმნო ეს საიტი</translation>
+<translation id="290376772003165898">გვერდის ტექსტი არ არის <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">ამ გვერდის გადათარგმნა ვერ მოხერხდა.</translation>
+<translation id="6040143037577758943">დახურვა</translation>
+<translation id="6831043979455480757">თარგმნა</translation>
+<translation id="7243308994586599757">ვარიანტები ხელმისაწვდომია ეკრანის ქვედა ნაწილთან</translation>
+<translation id="773466115871691567">ყოველთვის ითარგმნოს <ph name="SOURCE_LANGUAGE" /> გვერდები</translation>
<translation id="8298278839890148234">ვებ-ბრაუზერის აქტივობა</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb
index d18117e9c7f..2e23049928a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="kk">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> тіліндегі беттер ешқашан аударылмасын</translation>
+<translation id="124116460088058876">Қосымша тілдер</translation>
+<translation id="1285320974508926690">Бұл сайтты ешқашан аудармау</translation>
+<translation id="290376772003165898">Бет <ph name="LANGUAGE" /> тілінде емес пе?</translation>
+<translation id="5684874026226664614">Бұл бетті аудару мүмкін емес.</translation>
+<translation id="6040143037577758943">Жабу</translation>
+<translation id="6831043979455480757">Аудару</translation>
+<translation id="7243308994586599757">Опциялар экранның төменгі жағында тұрады</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> тіліндегі беттер әрқашан аударылсын</translation>
<translation id="8298278839890148234">Браузерді қолдану мәліметі</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb
index 312955fb520..79e7f51cb82 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="km">
+<translation id="1068672505746868501">កុំ​បកប្រែ​ទំព័រ​ជាភាសា <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">ភាសា​ច្រើន​ទៀត</translation>
+<translation id="1285320974508926690">មិនបកប្រែគេហទំព័រនេះទៀតឡើយ</translation>
+<translation id="290376772003165898">ទំព័រមិនមានជាភាសា <ph name="LANGUAGE" /> ទេ?</translation>
+<translation id="5684874026226664614">អូ។ ទំព័រនេះមិនអាចបកប្រែទេ។</translation>
+<translation id="6040143037577758943">បិទ</translation>
+<translation id="6831043979455480757">បកប្រែ</translation>
+<translation id="7243308994586599757">មានជម្រើសនៅក្បែរផ្នែកខាងក្រោមអេក្រង់</translation>
+<translation id="773466115871691567">បកប្រែ​ទំព័រ​ជាភាសា <ph name="SOURCE_LANGUAGE" /> ជានិច្ច</translation>
<translation id="8298278839890148234">សកម្មភាព​កម្មវិធីរុករក​តាមអ៊ីនធឺណិត</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb
index 91524cd5c0f..cabf85f2d68 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="kn">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ನಲ್ಲಿನ ಪುಟಗಳನ್ನು ಎಂದಿಗೂ ಅನುವಾದ ಮಾಡಬೇಡಿ</translation>
+<translation id="124116460088058876">ಹೆಚ್ಚಿನ ಭಾಷೆಗಳು</translation>
+<translation id="1285320974508926690">ಈ ಸೈಟ್ ಅನ್ನು ಎಂದಿಗೂ ಭಾಷಾಂತರಿಸದಿರಿ</translation>
+<translation id="290376772003165898"><ph name="LANGUAGE" /> ನಲ್ಲಿನ ಪುಟ ಇಲ್ಲವೇ?</translation>
+<translation id="5684874026226664614">ಓಹ್. ಈ ಪುಟವನ್ನು ಅನುವಾದಿಸಲಾಗುವುದಿಲ್ಲ.</translation>
+<translation id="6040143037577758943">ಮುಚ್ಚಿರಿ</translation>
+<translation id="6831043979455480757">ಅನುವಾದಿಸು</translation>
+<translation id="7243308994586599757">ಪರದೆಯ ಕೆಳಗೆ ಲಭ್ಯವಿರುವ ಆಯ್ಕೆಗಳು</translation>
+<translation id="773466115871691567">ಯಾವಾಗಲು <ph name="SOURCE_LANGUAGE" /> ನಲ್ಲಿನ ಪುಟಗಳನ್ನು ಅನುವಾದ ಮಾಡಿ</translation>
<translation id="8298278839890148234">ವೆಬ್ ಬ್ರೌಸಿಂಗ್ ಚಟುವಟಿಕೆ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb
index 742a5338444..de20f5dc367 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ko">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />로 된 페이지는 번역하지 않음</translation>
+<translation id="124116460088058876">다른 언어</translation>
+<translation id="1285320974508926690">이 사이트 번역 안함</translation>
+<translation id="290376772003165898">페이지 언어가 <ph name="LANGUAGE" />가 아닌가요?</translation>
+<translation id="5684874026226664614">죄송합니다. 이 페이지를 번역할 수 없습니다.</translation>
+<translation id="6040143037577758943">닫기</translation>
+<translation id="6831043979455480757">번역</translation>
+<translation id="7243308994586599757">화면 하단에서 옵션 선택 가능</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />로 된 페이지를 항상 번역</translation>
<translation id="8298278839890148234">웹브라우저 활동</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb
index 2d0f4b479b2..717031affb9 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ky">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> тилиндеги барактар эч качан которулбасын</translation>
+<translation id="124116460088058876">Дагы тилдер</translation>
+<translation id="1285320974508926690">Бул сайт эч качан которулбасын</translation>
+<translation id="290376772003165898">Барак <ph name="LANGUAGE" /> тилинде эмес бекен?</translation>
+<translation id="5684874026226664614">Ой, бул бет которулган жок.</translation>
+<translation id="6040143037577758943">Жабуу</translation>
+<translation id="6831043979455480757">Которуу</translation>
+<translation id="7243308994586599757">Параметрлер экрандын түбүндө берилген</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> тилиндеги барактар дайыма которулсун</translation>
<translation id="8298278839890148234">Көрүлгөн вебсайттар</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb
index fba8f18695e..7ab378b6803 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="lo">
+<translation id="1068672505746868501">ຢ່າແປໜ້າຕ່າງໆໃນ <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">ພາສາເພີ່ມເຕີມ</translation>
+<translation id="1285320974508926690">ຢ່າແປເວັບ​ໄຊ​ທ໌ນີ້</translation>
+<translation id="290376772003165898">ໜ້າບໍ່ຢູ່ໃນ <ph name="LANGUAGE" /> ບໍ?</translation>
+<translation id="5684874026226664614">ອຸ້ຍ. ບໍ່ສາມາດແປໜ້ານີ້ໄດ້.</translation>
+<translation id="6040143037577758943">ປິດ</translation>
+<translation id="6831043979455480757">ແປພາສາ</translation>
+<translation id="7243308994586599757">ທາງ​ເລືອກ​ມີ​ໃຫ້​ໃກ້​ປຸ່ມ​ຂອງ​ໜ້າ​ຈໍ</translation>
+<translation id="773466115871691567">ແປໜ້າຕ່າງໆສະເໝີໃນ <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">ການເຄື່ອນໄຫວໃນໂປຣແກຣມທ່ອງເວັບ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb
index a1a90e6ff7c..05d3d0706ff 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="lt">
+<translation id="1068672505746868501">Niekada neversti puslapių, parašytų <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Daugiau kalbų</translation>
+<translation id="1285320974508926690">Niekada neversti šios svetainės</translation>
+<translation id="290376772003165898">Puslapis parašytas ne <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Oi, nepavyko išversti šio puslapio.</translation>
+<translation id="6040143037577758943">Uždaryti</translation>
+<translation id="6831043979455480757">Vertėjas</translation>
+<translation id="7243308994586599757">Parinktys pasiekiamos netoli ekrano apačios</translation>
+<translation id="773466115871691567">Visada versti puslapius, parašytus <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Žiniatinklio naršyklės veikla</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb
index 5d965478866..d53de48768c 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="lv">
+<translation id="1068672505746868501">Nekad netulkot lapas šādā valodā: <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Citas valodas…</translation>
+<translation id="1285320974508926690">Nekad netulkot šo vietni</translation>
+<translation id="290376772003165898">Vai lapa nav šajā valodā: <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Hmm... Šo lapu nevarēja iztulkot.</translation>
+<translation id="6040143037577758943">Aizvērt</translation>
+<translation id="6831043979455480757">Tulkot</translation>
+<translation id="7243308994586599757">Opcijas, kas pieejamas ekrāna apakšējā daļā</translation>
+<translation id="773466115871691567">Vienmēr tulkot lapas šādā valodā: <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Pārlūkošanas darbības</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb
index 2f2d3c2b91b..82359fc6539 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="mk">
+<translation id="1068672505746868501">Никогаш не преведувај страници на <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Повеќе јазици</translation>
+<translation id="1285320974508926690">Никогаш не преведувај ја оваа локација</translation>
+<translation id="290376772003165898">Страницата не е на <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Оваа страница не може да се преведе.</translation>
+<translation id="6040143037577758943">Затвори</translation>
+<translation id="6831043979455480757">Преведи</translation>
+<translation id="7243308994586599757">Достапни се опции на дното на екранот</translation>
+<translation id="773466115871691567">Секогаш преведувај ги страниците на <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Активност на прелистувачот</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb
index d4a01802579..8d51f41f713 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ml">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ഭാഷയിലുള്ള പേജുകൾ ഒരിക്കലും വിവർത്തനം ചെയ്യരുത്</translation>
+<translation id="124116460088058876">കൂടുതൽ ഭാഷകൾ</translation>
+<translation id="1285320974508926690">ഈ സൈറ്റ് ഒരിക്കലും വിവര്‍‌ത്തനം ചെയ്യരുത്</translation>
+<translation id="290376772003165898">പേജ് <ph name="LANGUAGE" /> ഭാഷയിലല്ലേ?</translation>
+<translation id="5684874026226664614">ക്ഷമിക്കണം. ഈ പേജ് വിവർത്തനം ചെയ്യാനായില്ല.</translation>
+<translation id="6040143037577758943">അടയ്ക്കുക</translation>
+<translation id="6831043979455480757">വിവർത്തനം ചെയ്യുക</translation>
+<translation id="7243308994586599757">സ്‌ക്രീനിന്റെ ചുവടെ ഓപ്‌ഷനുകൾ ലഭ്യമാണ്</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> ഭാഷയിലുള്ള പേജുകൾ എപ്പോഴും വിവർത്തനം ചെയ്യുക</translation>
<translation id="8298278839890148234">വെബ് ബ്രൗസർ ആക്റ്റിവിറ്റി</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb
index 79d5dac7607..2f721cf0b56 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="mn">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> хэл дээрх хуудсыг хэзээ ч орчуулахгүй</translation>
+<translation id="124116460088058876">Бусад хэл</translation>
+<translation id="1285320974508926690">Энэ сайтыг хэзээ ч бүү хөрвүүл</translation>
+<translation id="290376772003165898">Хуудас <ph name="LANGUAGE" /> хэл дээр биш байна уу?</translation>
+<translation id="5684874026226664614">Өө. Энэ хуудсыг хөрвүүлж чадсангүй.</translation>
+<translation id="6040143037577758943">Хаах</translation>
+<translation id="6831043979455480757">Хөрвүүлэх</translation>
+<translation id="7243308994586599757">Дэлгэцийн доод хэсэгт сонголт боломжтой</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> хэл дээрх хуудсыг байнга орчуулна</translation>
<translation id="8298278839890148234">Хөтчийн үйл ажиллагаа</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb
index 5a3e6820752..66b36082e3d 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="mr">
+<translation id="1068672505746868501">पेज <ph name="SOURCE_LANGUAGE" /> मध्ये कधीही भाषांतरित करू नका</translation>
+<translation id="124116460088058876">आणखी भाषा...</translation>
+<translation id="1285320974508926690">या साइटचा कधीही भाषांतर करु नका</translation>
+<translation id="290376772003165898">पेज <ph name="LANGUAGE" />मध्ये नाही?</translation>
+<translation id="5684874026226664614">अरेरे. हे पृष्‍ठ भाषांतरित केले जाऊ शकले नाही.</translation>
+<translation id="6040143037577758943">बंद करा</translation>
+<translation id="6831043979455480757">भाषांतर करा</translation>
+<translation id="7243308994586599757">स्क्रीनच्या तळाशी पर्याय उपलब्ध आहेत</translation>
+<translation id="773466115871691567">नेहमी पेज <ph name="SOURCE_LANGUAGE" />मध्ये भाषांतरित करा</translation>
<translation id="8298278839890148234">वेब ब्राउझर अ‍ॅक्टिव्हिटी</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb
index e105952bce6..9bca4a8918a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ms">
+<translation id="1068672505746868501">Jangan sekali-kali terjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Lagi bahasa</translation>
+<translation id="1285320974508926690">Jangan sekali-kali menterjemahkan tapak ini</translation>
+<translation id="290376772003165898">Halaman bukan dalam <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Op. Halaman ini tidak dapat diterjemahkan.</translation>
+<translation id="6040143037577758943">Tutup</translation>
+<translation id="6831043979455480757">Terjemah</translation>
+<translation id="7243308994586599757">Pilihan tersedia berhampiran bahagian bawah skrin</translation>
+<translation id="773466115871691567">Sentiasa terjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktiviti penyemakan imbas</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb
index e8bf710e16c..ed292bfc27e 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="my">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ဘာသာ စာမျက်နှာများကို ဘယ်သောအခါမျှ ဘာသာမပြန်ရန်</translation>
+<translation id="124116460088058876">နောက်ထပ် ဘာသာစကားများ</translation>
+<translation id="1285320974508926690">ဒီဆိုက်ကို ဘယ်တော့မှ ဘာသာမပြန်ပါနှင့်</translation>
+<translation id="290376772003165898">စာမျက်နှာသည် <ph name="LANGUAGE" /> ဖြင့် ဟုတ်ပါသလား။</translation>
+<translation id="5684874026226664614">အူးပ်စ်။ ဒီစာမျက်နှာကို ဘာသာပြန် မရနိုင်ခဲ့ပါ။</translation>
+<translation id="6040143037577758943">ပိတ်ရန်</translation>
+<translation id="6831043979455480757">ဘာသာပြန်ရန်</translation>
+<translation id="7243308994586599757">ရွေးစရာများမှာ မျက်နှာပြင်၏ အောက်ခြေပိုင်းနားမှာ ရှိကြသည်</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> ဘာသာဖြင့် စာမျက်နှာများအားလုံးကို အမြဲဘာသာပြန်ရန်</translation>
<translation id="8298278839890148234">ဝဘ်ဘရောင်ဇာ လုပ်ဆောင်ချက်</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb
index 06ec47e44f4..11e137141d1 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ne">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> मा भएका पृष्ठहरूलाई कहिल्यै अनुवाद नगर्नुहोस्‌</translation>
+<translation id="124116460088058876">थप भाषाहरू</translation>
+<translation id="1285320974508926690">यो साइट कहिले पनि अनुवाद नगर्नुहोस्</translation>
+<translation id="290376772003165898"><ph name="LANGUAGE" /> भाषामा पृष्ठ छैन?</translation>
+<translation id="5684874026226664614">ओहो। यो पृष्ठ अनुवादन गर्न सकिएन।</translation>
+<translation id="6040143037577758943">बन्द गर्नुहोस्</translation>
+<translation id="6831043979455480757">अनुवाद गर्नुहोस्</translation>
+<translation id="7243308994586599757">विकल्पहरू स्क्रिनको तल नजिकै उपलब्ध छ</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> मा रहेका पृष्ठहरूलाई सधैँ अनुवाद गर्नुहोस्‌</translation>
<translation id="8298278839890148234">ब्राउजर प्रयोग गरी गरिएको क्रियाकलाप</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb
index 759b04e50cc..cd20f14e5ca 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="nl">
+<translation id="1068672505746868501">Pagina's in het <ph name="SOURCE_LANGUAGE" /> nooit vertalen</translation>
+<translation id="124116460088058876">Meer talen</translation>
+<translation id="1285320974508926690">Deze site nooit vertalen</translation>
+<translation id="290376772003165898">Is deze pagina niet in het <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Deze pagina kan niet worden vertaald.</translation>
+<translation id="6040143037577758943">Sluiten</translation>
+<translation id="6831043979455480757">Vertalen</translation>
+<translation id="7243308994586599757">Opties beschikbaar onder aan het scherm</translation>
+<translation id="773466115871691567">Pagina's in het <ph name="SOURCE_LANGUAGE" /> altijd vertalen</translation>
<translation id="8298278839890148234">Webbrowseractivititeit</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb
index 409a800ee70..0a02095d40b 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="no">
+<translation id="1068672505746868501">Oversett aldri sider på <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Flere språk</translation>
+<translation id="1285320974508926690">Oversett aldri dette nettstedet</translation>
+<translation id="290376772003165898">Er ikke siden på <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Beklager. Denne siden kunne ikke oversettes.</translation>
+<translation id="6040143037577758943">Lukk</translation>
+<translation id="6831043979455480757">Oversett</translation>
+<translation id="7243308994586599757">Du finner alternativer ved bunnen av skjermen</translation>
+<translation id="773466115871691567">Oversett alltid sider på <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Nettleseraktivitet</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb
index f88f14b33c3..8d046c770c1 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="or">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />ରେ ପୃଷ୍ଠାଗୁଡ଼ିକୁ ଅନୁବାଦ କରନ୍ତୁ ନାହିଁ</translation>
+<translation id="124116460088058876">ଅନେକ ଭାଷା</translation>
+<translation id="1285320974508926690">ଏହି ସାଇଟ୍‍କୁ କଦାପି ଅନୁବାଦ କରନ୍ତୁ ନାହିଁ</translation>
+<translation id="290376772003165898">ପୃଷ୍ଠାଟି <ph name="LANGUAGE" />ରେ ନାହିଁ?</translation>
+<translation id="5684874026226664614">ଓହୋଃ! ଏହି ପୃଷ୍ଠା ଅନୁବାଦ କରାଯାଇପାରିଲା ନାହିଁ।</translation>
+<translation id="6040143037577758943">ବନ୍ଦ</translation>
+<translation id="6831043979455480757">ଅନୁବାଦ କରନ୍ତୁ</translation>
+<translation id="7243308994586599757">ସ୍କ୍ରିନ୍‍ର ନିମ୍ନରେ ବିକଳ୍ପଗୁଡ଼ିକ ଉପଲବ୍ଧ ଅଛି</translation>
+<translation id="773466115871691567">ସର୍ବଦା <ph name="SOURCE_LANGUAGE" />ରେ ପୃଷ୍ଠାଗୁଡ଼ିକୁ ଅନୁବାଦ କରନ୍ତୁ</translation>
<translation id="8298278839890148234">ୱେବ୍ ବ୍ରାଉଜର୍ କାର୍ଯ୍ୟକଳାପ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb
index 0d4c8c78fba..6394a67f909 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="pa">
+<translation id="1068672505746868501">ਕਦੇ ਵੀ ਪੰਨਿਆਂ ਦਾ ਅਨੁਵਾਦ <ph name="SOURCE_LANGUAGE" /> ਵਿੱਚ ਨਾ ਕਰੋ</translation>
+<translation id="124116460088058876">ਹੋਰ ਭਾਸ਼ਾਵਾਂ</translation>
+<translation id="1285320974508926690">ਕਦੇ ਵੀ ਇਸ ਸਾਈਟ ਦਾ ਅਨੁਵਾਦ ਨਾ ਕਰੋ</translation>
+<translation id="290376772003165898">ਕੀ ਪੰਨਾ <ph name="LANGUAGE" /> ਵਿੱਚ ਨਹੀਂ ਹੈ?</translation>
+<translation id="5684874026226664614">ਓਹੋ। ਇਸ ਪੰਨੇ ਦਾ ਅਨੁਵਾਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।</translation>
+<translation id="6040143037577758943">ਬੰਦ ਕਰੋ</translation>
+<translation id="6831043979455480757">ਅਨੁਵਾਦ ਕਰੋ</translation>
+<translation id="7243308994586599757">ਵਿਕਲਪ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਪਾਸੇ ਦੇ ਕੋਲ ਉਪਲਬਧ ਹਨ</translation>
+<translation id="773466115871691567">ਪੰਨਿਆਂ ਦਾ ਅਨੁਵਾਦ ਹਮੇਸ਼ਾਂ <ph name="SOURCE_LANGUAGE" /> ਭਾਸ਼ਾ ਵਿੱਚ ਕਰੋ</translation>
<translation id="8298278839890148234">ਵੈੱਬ ਬ੍ਰਾਊਜ਼ਰ ਸਰਗਰਮੀ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb
index 52a394f3e02..2185aeb5d3d 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="pl">
+<translation id="1068672505746868501">Nigdy nie tłumacz stron, których językiem jest <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Więcej języków</translation>
+<translation id="1285320974508926690">Nigdy nie tłumacz tej witryny</translation>
+<translation id="290376772003165898">Język tej strony to nie <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Nie można przetłumaczyć tej strony.</translation>
+<translation id="6040143037577758943">Zamknij</translation>
+<translation id="6831043979455480757">Tłumacz</translation>
+<translation id="7243308994586599757">Opcje dostępne u dołu ekranu</translation>
+<translation id="773466115871691567">Zawsze tłumacz strony, których językiem jest <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktywność w przeglądarce</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb
index dd6972f79e3..45880dbbefa 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="pt-BR">
+<translation id="1068672505746868501">Nunca traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Mais idiomas</translation>
+<translation id="1285320974508926690">Nunca traduzir este site</translation>
+<translation id="290376772003165898">A página não está em <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Não foi possível traduzir esta página.</translation>
+<translation id="6040143037577758943">Fechar</translation>
+<translation id="6831043979455480757">Traduzir</translation>
+<translation id="7243308994586599757">Opções disponíveis perto da parte inferior da tela</translation>
+<translation id="773466115871691567">Sempre traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Atividade do navegador da Web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb
index 76966566f3d..0b06168100d 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="pt-PT">
+<translation id="1068672505746868501">Nunca traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Mais idiomas</translation>
+<translation id="1285320974508926690">Nunca traduzir este site</translation>
+<translation id="290376772003165898">A página não está em <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups! Não foi possível traduzir esta página.</translation>
+<translation id="6040143037577758943">Fechar</translation>
+<translation id="6831043979455480757">Traduzir</translation>
+<translation id="7243308994586599757">Opções disponíveis junto à parte inferior do ecrã</translation>
+<translation id="773466115871691567">Traduza sempre páginas em <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Atividade do navegador de Internet</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb
index 12f45d28945..87a6bce7c07 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ro">
+<translation id="1068672505746868501">Nu traduce niciodată paginile în <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Mai multe limbi</translation>
+<translation id="1285320974508926690">Nu traduce niciodată acest site</translation>
+<translation id="290376772003165898">Pagina nu este în <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Hopa. Această pagină nu a putut fi tradusă.</translation>
+<translation id="6040143037577758943">Închide</translation>
+<translation id="6831043979455480757">Tradu</translation>
+<translation id="7243308994586599757">Opțiuni disponibile în partea de jos a ecranului</translation>
+<translation id="773466115871691567">Tradu întotdeauna paginile în <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Activitatea în browserul web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb
index 401647df65a..733f199d730 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ru">
+<translation id="1068672505746868501">Не переводить страницы на этом языке: <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Другие языки</translation>
+<translation id="1285320974508926690">Никогда не переводить этот сайт</translation>
+<translation id="290376772003165898">Язык страницы не <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Не удалось перевести страницу</translation>
+<translation id="6040143037577758943">Закрыть</translation>
+<translation id="6831043979455480757">Перевести</translation>
+<translation id="7243308994586599757">Доступные параметры указаны в нижней части экрана</translation>
+<translation id="773466115871691567">Переводить страницы на этом языке: <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Действия в веб-браузере</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb
index 1e95ef55536..90b6f032c71 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="si">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> හි පිටු කිසිදාක පරිවර්තනය කරන්න එපා</translation>
+<translation id="124116460088058876">තවත් භාෂා</translation>
+<translation id="1285320974508926690">මෙම අඩවිය කිසිවිට පරිවර්තනය නොකරන්න</translation>
+<translation id="290376772003165898">පිටුව <ph name="LANGUAGE" /> බසින් නොවේ ද?</translation>
+<translation id="5684874026226664614">අපොයි. මෙම පිටුව පැටවිය නොහැකි විය.</translation>
+<translation id="6040143037577758943">වසන්න</translation>
+<translation id="6831043979455480757">පරිවර්තනය කරන්න</translation>
+<translation id="7243308994586599757">තිරයේ පහළට ආසන්නව විකල්ප ලබා ගත හැකිය</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> හි පිටු සැමවිටම පරිවර්තනය කරන්න</translation>
<translation id="8298278839890148234">වෙබ් බ්‍රවුසර ක්‍රියාකාරකම</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb
index 53b2d4ec263..fe4bb0c1612 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sk">
+<translation id="1068672505746868501">Nikdy neprekladať stránky v jazyku <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Ďalšie jazyky</translation>
+<translation id="1285320974508926690">Nikdy neprekladať tieto webové stránky</translation>
+<translation id="290376772003165898">Stránka nie je v jazyku <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Hops. Túto stránku nebolo možné preložiť.</translation>
+<translation id="6040143037577758943">Zavrieť</translation>
+<translation id="6831043979455480757">Preložiť</translation>
+<translation id="7243308994586599757">Možnosti sú k dispozícii v dolnej časti obrazovky</translation>
+<translation id="773466115871691567">Vždy prekladať stránky v jazyku <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktivita webového prehliadača</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb
index 152968ea560..bc91c54ae43 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sl">
+<translation id="1068672505746868501">Nikoli ne prevedi strani v jeziku <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Več jezikov</translation>
+<translation id="1285320974508926690">Nikoli ne prevedi tega spletnega mesta</translation>
+<translation id="290376772003165898">Stran ni v jeziku <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ojoj, te strani bi bilo mogoče prevesti.</translation>
+<translation id="6040143037577758943">Zapri</translation>
+<translation id="6831043979455480757">Prevedi</translation>
+<translation id="7243308994586599757">Možnosti so na voljo pri dnu zaslona</translation>
+<translation id="773466115871691567">Vedno prevedi strani v jeziku <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Dejavnost brskalnika</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb
index 2e0a6950797..c88ab6c0cfc 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sq">
+<translation id="1068672505746868501">Mos përkthe asnjëherë faqet që janë në <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Gjuhë të tjera</translation>
+<translation id="1285320974508926690">Asnjëherë mos e përkthe këtë sajt</translation>
+<translation id="290376772003165898">Faqja nuk është në <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Mos! Kjo faqe nuk mund të përkthehej.</translation>
+<translation id="6040143037577758943">Mbyll</translation>
+<translation id="6831043979455480757">Përkthe</translation>
+<translation id="7243308994586599757">Opsionet janë të disponueshme pranë fundit të ekranit</translation>
+<translation id="773466115871691567">Përkthe gjithmonë faqet që janë në <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktiviteti i shfletuesit të uebit</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb
index 24f260082a7..d66789dacf8 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sr-Latn">
+<translation id="1068672505746868501">Nikad ne prevodi stranice na jeziku <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Još jezika</translation>
+<translation id="1285320974508926690">Nikad ne prevodi ovaj sajt</translation>
+<translation id="290376772003165898">Ova stranica nije na jeziku <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Ups, prevođenje ove stranice nije uspelo.</translation>
+<translation id="6040143037577758943">Zatvori</translation>
+<translation id="6831043979455480757">Prevedi</translation>
+<translation id="7243308994586599757">Opcije su dostupne u dnu ekrana</translation>
+<translation id="773466115871691567">Uvek predvodi stranice na jeziku <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Aktivnosti u veb-pregledaču</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb
index 9008032cbef..d5eb9aa0fef 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sr">
+<translation id="1068672505746868501">Никад не преводи странице на језику <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Још језика</translation>
+<translation id="1285320974508926690">Никад не преводи овај сајт</translation>
+<translation id="290376772003165898">Ова страница није на језику <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Упс, превођење ове странице није успело.</translation>
+<translation id="6040143037577758943">Затвори</translation>
+<translation id="6831043979455480757">Преведи</translation>
+<translation id="7243308994586599757">Опције су доступне у дну екрана</translation>
+<translation id="773466115871691567">Увек предводи странице на језику <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Активности у веб-прегледачу</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb
index 610cb47e53f..532dc6eb359 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sv">
+<translation id="1068672505746868501">Översätt aldrig sidor på <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Fler språk</translation>
+<translation id="1285320974508926690">Översätt aldrig den här webbplatsen</translation>
+<translation id="290376772003165898">Är sidan inte på <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Det gick inte att översätta sidan.</translation>
+<translation id="6040143037577758943">Stäng</translation>
+<translation id="6831043979455480757">Översätt</translation>
+<translation id="7243308994586599757">Alternativ visas nära skärmens nedre kant</translation>
+<translation id="773466115871691567">Översätt alltid sidor på <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Webbläsaraktivitet</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb
index cf0a4b711da..2854041492f 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="sw">
+<translation id="1068672505746868501">Usiwahi kutafsiri kurasa katika lugha ya <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Lugha zaidi</translation>
+<translation id="1285320974508926690">Kamwe usitafsiri tovuti hii</translation>
+<translation id="290376772003165898">Je, ukurasa huu haupo katika <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Lo! Ukurasa huu haukuweza kutafsiriwa.</translation>
+<translation id="6040143037577758943">Funga</translation>
+<translation id="6831043979455480757">Tafsiri</translation>
+<translation id="7243308994586599757">Chaguo zinapatikana karibu na sehemu ya chini ya skrini</translation>
+<translation id="773466115871691567">Zitafsiri kurasa katika <ph name="SOURCE_LANGUAGE" /> wakati wote</translation>
<translation id="8298278839890148234">Shughuli za kivinjari</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb
index 8a47545a26a..c7f7130e4c5 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ta">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> மொழியில் உள்ள பக்கங்களை ஒருபோதும் மொழிபெயர்க்காதே</translation>
+<translation id="124116460088058876">மேலும் மொழிகள்</translation>
+<translation id="1285320974508926690">இந்த தளத்தை எப்போதும் மொழிபெயர்க்க வேண்டாம்</translation>
+<translation id="290376772003165898"><ph name="LANGUAGE" /> மொழியில் பக்கம் இல்லையா?</translation>
+<translation id="5684874026226664614">அச்சச்சோ. இந்தப் பக்கத்தை மொழிபெயர்க்க முடியாது.</translation>
+<translation id="6040143037577758943">மூடு</translation>
+<translation id="6831043979455480757">மொழிபெயர்</translation>
+<translation id="7243308994586599757">திரையின் கீழ்ப்பகுதிக்கு அருகில் கிடைக்கும் விருப்பங்கள்</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> மொழியில் உள்ள பக்கங்களை எப்போதும் மொழிபெயர்</translation>
<translation id="8298278839890148234">இணைய உலாவியின் செயல்பாடு</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb
index 3894c532d87..53279940602 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="te">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />లో ఉన్న పేజీలను ఎప్పుడూ అనువదించవద్దు</translation>
+<translation id="124116460088058876">మరిన్ని భాషలు</translation>
+<translation id="1285320974508926690">ఈ సైట్‌ను ఎప్పటికీ అనువదించవద్దు</translation>
+<translation id="290376772003165898">పేజీ <ph name="LANGUAGE" />లో లేదా?</translation>
+<translation id="5684874026226664614">అయ్యో. ఈ పేజీని అనువదించడం సాధ్యపడలేదు.</translation>
+<translation id="6040143037577758943">మూసివేయి</translation>
+<translation id="6831043979455480757">అనువదించు</translation>
+<translation id="7243308994586599757">స్క్రీన్ దిగువభాగం సమీపంలో ఎంపికలు అందుబాటులో ఉంటాయి</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />లో ఉన్న పేజీలను ఎల్లప్పుడూ అనువదించు</translation>
<translation id="8298278839890148234">బ్రౌజింగ్ యాక్టివిటీ</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb
index 1c8e14c9ca0..024e4b547a9 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="th">
+<translation id="1068672505746868501">ไม่ต้องแปลหน้าเว็บภาษา<ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">ภาษาเพิ่มเติม</translation>
+<translation id="1285320974508926690">ไม่ต้องแปลเว็บไซต์นี้</translation>
+<translation id="290376772003165898">หน้านี้ไม่ใช่ภาษา<ph name="LANGUAGE" />ใช่ไหม</translation>
+<translation id="5684874026226664614">อ๊ะ หน้านี้ไม่สามารถแปลได้</translation>
+<translation id="6040143037577758943">ปิด</translation>
+<translation id="6831043979455480757">แปลภาษา</translation>
+<translation id="7243308994586599757">มีตัวเลือกอยู่ทางด้านล่างของหน้าจอ</translation>
+<translation id="773466115871691567">แปลหน้าเว็บภาษา<ph name="SOURCE_LANGUAGE" />ทุกครั้ง</translation>
<translation id="8298278839890148234">กิจกรรมของเว็บเบราว์เซอร์</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb
index 11d4ab4aeef..2f9a035ea1b 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="tr">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> dilindeki sayfaları asla çevirme</translation>
+<translation id="124116460088058876">Diğer diller</translation>
+<translation id="1285320974508926690">Bu siteyi hiçbir zaman çevirme</translation>
+<translation id="290376772003165898">Sayfa <ph name="LANGUAGE" /> dilinde değil mi?</translation>
+<translation id="5684874026226664614">Hata! Bu sayfa çevrilemedi.</translation>
+<translation id="6040143037577758943">Kapat</translation>
+<translation id="6831043979455480757">Çevir</translation>
+<translation id="7243308994586599757">Sayfanın altına yakın bir yerde kullanılabilen seçenekler</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> dilindeki sayfaları her zaman çevir</translation>
<translation id="8298278839890148234">Web tarayıcısı etkinliği</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb
index f8630458317..4a29de8edb5 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="uk">
+<translation id="1068672505746868501">Ніколи не перекладати сторінки такою мовою: <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Інші мови</translation>
+<translation id="1285320974508926690">Ніколи не перекладати цей сайт</translation>
+<translation id="290376772003165898">Ця сторінка відображається не такою мовою: <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">На жаль, цю сторінку неможливо перекласти.</translation>
+<translation id="6040143037577758943">Закрити</translation>
+<translation id="6831043979455480757">Перекласти</translation>
+<translation id="7243308994586599757">Опції можна знайти внизу екрана</translation>
+<translation id="773466115871691567">Завжди перекладати сторінки такою мовою: <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Дії у веб-переглядачі</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb
index 57e46884972..4113ad7cdaa 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ur">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> کے صفحات کا کبھی ترجمہ نہ کریں</translation>
+<translation id="124116460088058876">مزید زبانیں</translation>
+<translation id="1285320974508926690">اس سائٹ کا ترجمہ کبھی نہ کریں</translation>
+<translation id="290376772003165898">صفحہ <ph name="LANGUAGE" /> میں نہیں ہے؟</translation>
+<translation id="5684874026226664614">افوہ۔ اس صفحہ کا ترجمہ نہیں کیا جا سکا۔</translation>
+<translation id="6040143037577758943">بند کریں</translation>
+<translation id="6831043979455480757">ترجمہ کریں</translation>
+<translation id="7243308994586599757">اسکرین کے نچلے حصہ کے قریب اختیارات دستیاب ہیں</translation>
+<translation id="773466115871691567">ہمیشہ <ph name="SOURCE_LANGUAGE" /> کے صفحات کا ترجمہ کریں</translation>
<translation id="8298278839890148234">ویب براؤزر کی سرگرمی</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb
index 92655c71edf..f0af58d90cd 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="uz">
+<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> tilidagi sahifalar hech qachon tarjima qilinmasin</translation>
+<translation id="124116460088058876">Boshqa tillar</translation>
+<translation id="1285320974508926690">Bu sayt hech qachon tarjima qilinmasin</translation>
+<translation id="290376772003165898">Sahifa <ph name="LANGUAGE" /> tilida emasmi?</translation>
+<translation id="5684874026226664614">Bu sahifani tarjima qilib bo‘lmadi.</translation>
+<translation id="6040143037577758943">Yopish</translation>
+<translation id="6831043979455480757">Tarjima</translation>
+<translation id="7243308994586599757">Parametrlar ekranning quyi qismiga yaqinroq joyda</translation>
+<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> tilidagi sahifalar doim tarjima qilinsin</translation>
<translation id="8298278839890148234">Veb-brauzerdagi faoliyat</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb
index 2f57bd60947..2744201fd71 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="vi">
+<translation id="1068672505746868501">Không bao giờ dịch các trang viết bằng <ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Ngôn ngữ khác</translation>
+<translation id="1285320974508926690">Không bao giờ dịch trang web này</translation>
+<translation id="290376772003165898">Trang này không được viết bằng <ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Rất tiếc. Không thể dịch trang này.</translation>
+<translation id="6040143037577758943">Đóng</translation>
+<translation id="6831043979455480757">Dịch</translation>
+<translation id="7243308994586599757">Có các tùy chọn ở gần cuối màn hình</translation>
+<translation id="773466115871691567">Luôn dịch các trang viết bằng <ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Hoạt động duyệt web</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb
index 84075ec9807..0c3183bb774 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-CN">
+<translation id="1068672505746868501">一律不翻译<ph name="SOURCE_LANGUAGE" />网页</translation>
+<translation id="124116460088058876">更多语言</translation>
+<translation id="1285320974508926690">一律不翻译此网站</translation>
+<translation id="290376772003165898">网页的源语言不是<ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">糟糕,此网页内容无法翻译。</translation>
+<translation id="6040143037577758943">关闭</translation>
+<translation id="6831043979455480757">翻译</translation>
+<translation id="7243308994586599757">选项在靠近屏幕底部的位置</translation>
+<translation id="773466115871691567">一律翻译<ph name="SOURCE_LANGUAGE" />网页</translation>
<translation id="8298278839890148234">网络浏览器活动</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb
index abd42fba040..3b6aab868dd 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-HK">
+<translation id="1068672505746868501">永不翻譯來源語言為<ph name="SOURCE_LANGUAGE" />的網頁</translation>
+<translation id="124116460088058876">更多語言</translation>
+<translation id="1285320974508926690">永不翻譯此網站</translation>
+<translation id="290376772003165898">網頁的來源語言不是<ph name="LANGUAGE" />嗎?</translation>
+<translation id="5684874026226664614">糟糕!系統無法翻譯這個網頁的內容。</translation>
+<translation id="6040143037577758943">關閉</translation>
+<translation id="6831043979455480757">翻譯</translation>
+<translation id="7243308994586599757">您可在畫面底部附近找到選項</translation>
+<translation id="773466115871691567">一律翻譯來源語言為<ph name="SOURCE_LANGUAGE" />的網頁</translation>
<translation id="8298278839890148234">網絡瀏覽器活動</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb
index ecea64ea7f4..d338d98229a 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-TW">
+<translation id="1068672505746868501">一律不翻譯<ph name="SOURCE_LANGUAGE" />網頁</translation>
+<translation id="124116460088058876">更多語言</translation>
+<translation id="1285320974508926690">一律不翻譯此網站</translation>
+<translation id="290376772003165898">不是<ph name="LANGUAGE" />網頁嗎?</translation>
+<translation id="5684874026226664614">糟糕!系統無法翻譯這個網頁的內容。</translation>
+<translation id="6040143037577758943">關閉</translation>
+<translation id="6831043979455480757">翻譯</translation>
+<translation id="7243308994586599757">選項在接近畫面底部的位置</translation>
+<translation id="773466115871691567">一律翻譯<ph name="SOURCE_LANGUAGE" />網頁</translation>
<translation id="8298278839890148234">網路瀏覽器活動</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb
index 1da5d91a768..9e01afbe908 100644
--- a/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb
+++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb
@@ -1,5 +1,14 @@
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zu">
+<translation id="1068672505746868501">Ungahumushi amakhasi ngesi-<ph name="SOURCE_LANGUAGE" /></translation>
+<translation id="124116460088058876">Izilimi eziningi</translation>
+<translation id="1285320974508926690">Ungalokothi uhumushe leli sayithi</translation>
+<translation id="290376772003165898">Ikhasi alikho ngesi-<ph name="LANGUAGE" />?</translation>
+<translation id="5684874026226664614">Eshu. Leli khasi alikwazi ukuhunyushwa.</translation>
+<translation id="6040143037577758943">Vala</translation>
+<translation id="6831043979455480757">Humusha</translation>
+<translation id="7243308994586599757">Izinketho ziyatholakala eduze kwangaphansi kwesikrini</translation>
+<translation id="773466115871691567">Njalo humusha amakhasi ngesi-<ph name="SOURCE_LANGUAGE" /></translation>
<translation id="8298278839890148234">Umsebenzi wesiphequluli sewebhu</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/weblayer/browser/java/weblayer_strings.grd b/chromium/weblayer/browser/java/weblayer_strings.grd
index cd1e72cc645..79a4dcddd27 100644
--- a/chromium/weblayer/browser/java/weblayer_strings.grd
+++ b/chromium/weblayer/browser/java/weblayer_strings.grd
@@ -172,6 +172,34 @@
<message name="IDS_WEBLAYER_NOTIFICATION_CHANNEL_GROUP_NAME" desc="The user-facing label for the notification channel group used for notifications arising from web browsing activity.">
Web browser activity
</message>
+ <message name="IDS_WEBLAYER_BOTTOM_BAR_SCREEN_POSITION" desc="Accessibility label to inform users about the InfoBar location">
+ Options available near bottom of the screen
+ </message>
+ <message name="IDS_WEBLAYER_INFOBAR_CLOSE" desc="Accessibility label for the dismiss infobar Button">
+ Close
+ </message>
+ <!-- TranslateInfoBar -->
+ <message name="IDS_TRANSLATE_INFOBAR_ERROR">
+ Oops. This page could not be translated.
+ </message>
+ <message name="IDS_TRANSLATE_BUTTON" desc="Possible texts to display on the translate infobar buttons. [CHAR-LIMIT=24]">
+ Translate
+ </message>
+ <message name="IDS_TRANSLATE_NEVER_TRANSLATE_SITE" desc="Text to display on the never translate site (like www.google.com) button. [CHAR-LIMIT=64]">
+ Never translate this site
+ </message>
+ <message name="IDS_TRANSLATE_OPTION_ALWAYS_TRANSLATE" desc="Option in the Chrome menu. User can click the 'Always Translate' option to indicate that they want Chrome to translate pages in this language automatically. Imperative.">
+ Always translate pages in <ph name="SOURCE_LANGUAGE">%1$s<ex>French</ex></ph>
+ </message>
+ <message name="IDS_TRANSLATE_OPTION_NEVER_TRANSLATE" desc="Option in the Chrome menu. User can click the 'Never Translate' option to indicate that they never want Chrome to translate pages in this language. The variable SOURCE_LANGUAGE could be any of 50+ languages supported by Google Translate, like French, Spanish, German, Italian, Japanese, Korean, etc. Imperative.">
+ Never translate pages in <ph name="SOURCE_LANGUAGE">%1$s<ex>French</ex></ph>
+ </message>
+ <message name="IDS_TRANSLATE_OPTION_MORE_LANGUAGE" desc="Option in the Chrome menu. Lets the user open a dialog to choose other target languages for translation, from a list of available languages. [CHAR-LIMIT=64]">
+ More languages
+ </message>
+ <message name="IDS_TRANSLATE_OPTION_NOT_SOURCE_LANGUAGE" desc="Option in the Chrome menu. Sometimes a web page's source language is not correctly identified by Google Translate, and this menu option lets the user open a submenu to select another language as the source language to translate. Phrased as a question as if to query the user, 'Is this page not in [source language identified]? If so, click here.' [CHAR-LIMIT=64]">
+ Page is not in <ph name="LANGUAGE">%1$s<ex>French</ex></ph>?
+ </message>
</messages>
</release>
</grit>