summaryrefslogtreecommitdiff
path: root/chromium/weblayer/browser
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
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.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')
-rw-r--r--chromium/weblayer/browser/DEPS14
-rw-r--r--chromium/weblayer/browser/android/javatests/BUILD.gn41
-rw-r--r--chromium/weblayer/browser/android/javatests/skew/expectations.txt56
-rw-r--r--chromium/weblayer/browser/android/metrics/DEPS1
-rw-r--r--chromium/weblayer/browser/android/metrics/weblayer_metrics_service_accessor.h28
-rw-r--r--chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc126
-rw-r--r--chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h20
-rw-r--r--chromium/weblayer/browser/android/resource_mapper.cc1
-rw-r--r--chromium/weblayer/browser/autofill_client_impl.cc13
-rw-r--r--chromium/weblayer/browser/autofill_client_impl.h7
-rw-r--r--chromium/weblayer/browser/browser_context_impl.cc29
-rw-r--r--chromium/weblayer/browser/browser_context_impl.h5
-rw-r--r--chromium/weblayer/browser/browser_controls_container_view.cc56
-rw-r--r--chromium/weblayer/browser/browser_controls_container_view.h9
-rw-r--r--chromium/weblayer/browser/browser_controls_navigation_state_handler.cc13
-rw-r--r--chromium/weblayer/browser/browser_controls_navigation_state_handler.h2
-rw-r--r--chromium/weblayer/browser/browser_impl.cc198
-rw-r--r--chromium/weblayer/browser/browser_impl.h20
-rw-r--r--chromium/weblayer/browser/browser_list.cc66
-rw-r--r--chromium/weblayer/browser/browser_list.h56
-rw-r--r--chromium/weblayer/browser/browser_list_observer.h27
-rw-r--r--chromium/weblayer/browser/browser_main_parts_impl.cc17
-rw-r--r--chromium/weblayer/browser/browsing_data_remover_delegate.cc54
-rw-r--r--chromium/weblayer/browser/browsing_data_remover_delegate.h58
-rw-r--r--chromium/weblayer/browser/browsing_data_remover_delegate_factory.cc46
-rw-r--r--chromium/weblayer/browser/browsing_data_remover_delegate_factory.h41
-rw-r--r--chromium/weblayer/browser/confirm_infobar_android.cc95
-rw-r--r--chromium/weblayer/browser/confirm_infobar_android.h40
-rw-r--r--chromium/weblayer/browser/content_browser_client_impl.cc161
-rw-r--r--chromium/weblayer/browser/content_browser_client_impl.h25
-rw-r--r--chromium/weblayer/browser/content_view_render_view.cc26
-rw-r--r--chromium/weblayer/browser/content_view_render_view.h6
-rw-r--r--chromium/weblayer/browser/cookie_manager_impl.cc6
-rw-r--r--chromium/weblayer/browser/download_manager_delegate_impl.cc11
-rw-r--r--chromium/weblayer/browser/feature_list_creator.cc3
-rw-r--r--chromium/weblayer/browser/http_auth_handler_impl.cc50
-rw-r--r--chromium/weblayer/browser/http_auth_handler_impl.h40
-rw-r--r--chromium/weblayer/browser/infobar_android.cc91
-rw-r--r--chromium/weblayer/browser/infobar_android.h86
-rw-r--r--chromium/weblayer/browser/infobar_container_android.cc114
-rw-r--r--chromium/weblayer/browser/infobar_container_android.h58
-rw-r--r--chromium/weblayer/browser/infobar_service.cc25
-rw-r--r--chromium/weblayer/browser/infobar_service.h50
-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
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_browsertest.cc112
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.cc25
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.h38
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.cc70
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.h38
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.cc52
-rw-r--r--chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.h47
-rw-r--r--chromium/weblayer/browser/navigation_browsertest.cc22
-rw-r--r--chromium/weblayer/browser/navigation_controller_impl.cc33
-rw-r--r--chromium/weblayer/browser/navigation_controller_impl.h6
-rw-r--r--chromium/weblayer/browser/navigation_impl.cc14
-rw-r--r--chromium/weblayer/browser/new_tab_callback_proxy.cc4
-rw-r--r--chromium/weblayer/browser/new_tab_callback_proxy.h2
-rw-r--r--chromium/weblayer/browser/password_manager_driver_factory.cc132
-rw-r--r--chromium/weblayer/browser/password_manager_driver_factory.h61
-rw-r--r--chromium/weblayer/browser/permissions/weblayer_permissions_client.cc8
-rw-r--r--chromium/weblayer/browser/permissions/weblayer_permissions_client.h2
-rw-r--r--chromium/weblayer/browser/persistence/browser_persistence_common.cc5
-rw-r--r--chromium/weblayer/browser/persistence/browser_persister.cc22
-rw-r--r--chromium/weblayer/browser/persistence/browser_persister.h16
-rw-r--r--chromium/weblayer/browser/persistence/browser_persister_browsertest.cc156
-rw-r--r--chromium/weblayer/browser/persistence/browser_persister_file_utils.cc91
-rw-r--r--chromium/weblayer/browser/persistence/browser_persister_file_utils.h39
-rw-r--r--chromium/weblayer/browser/persistence/minimal_browser_persister_browsertest.cc4
-rw-r--r--chromium/weblayer/browser/popup_blocker_browsertest.cc265
-rw-r--r--chromium/weblayer/browser/popup_navigation_delegate_impl.cc69
-rw-r--r--chromium/weblayer/browser/popup_navigation_delegate_impl.h42
-rw-r--r--chromium/weblayer/browser/prefetch_browsertest.cc125
-rw-r--r--chromium/weblayer/browser/profile_browsertest.cc27
-rw-r--r--chromium/weblayer/browser/profile_disk_operations.cc1
-rw-r--r--chromium/weblayer/browser/profile_disk_operations_unittests.cc18
-rw-r--r--chromium/weblayer/browser/profile_impl.cc199
-rw-r--r--chromium/weblayer/browser/profile_impl.h43
-rw-r--r--chromium/weblayer/browser/safe_browsing/BUILD.gn41
-rw-r--r--chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc60
-rw-r--r--chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h53
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_blocking_page.cc18
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc140
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc74
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_service.h9
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc30
-rw-r--r--chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h12
-rw-r--r--chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc19
-rw-r--r--chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.h4
-rw-r--r--chromium/weblayer/browser/site_isolation_browsertest.cc256
-rw-r--r--chromium/weblayer/browser/site_isolation_policy_unittest.cc113
-rw-r--r--chromium/weblayer/browser/ssl_browsertest.cc43
-rw-r--r--chromium/weblayer/browser/system_network_context_manager.cc7
-rw-r--r--chromium/weblayer/browser/tab_impl.cc316
-rw-r--r--chromium/weblayer/browser/tab_impl.h66
-rw-r--r--chromium/weblayer/browser/tab_specific_content_settings_delegate.cc11
-rw-r--r--chromium/weblayer/browser/tab_specific_content_settings_delegate.h2
-rw-r--r--chromium/weblayer/browser/translate_browsertest.cc488
-rw-r--r--chromium/weblayer/browser/translate_client_impl.cc38
-rw-r--r--chromium/weblayer/browser/translate_client_impl.h17
-rw-r--r--chromium/weblayer/browser/translate_compact_infobar.cc246
-rw-r--r--chromium/weblayer/browser/translate_compact_infobar.h112
-rw-r--r--chromium/weblayer/browser/translate_ranker_factory.cc5
-rw-r--r--chromium/weblayer/browser/translate_ranker_factory.h14
-rw-r--r--chromium/weblayer/browser/translate_utils.cc54
-rw-r--r--chromium/weblayer/browser/translate_utils.h43
-rw-r--r--chromium/weblayer/browser/ukm_browsertest.cc158
-rw-r--r--chromium/weblayer/browser/url_bar/url_bar_browsertest.cc15
-rw-r--r--chromium/weblayer/browser/verdict_cache_manager_factory.cc42
-rw-r--r--chromium/weblayer/browser/verdict_cache_manager_factory.h51
-rw-r--r--chromium/weblayer/browser/weblayer_browser_interface_binders.cc18
-rw-r--r--chromium/weblayer/browser/weblayer_features.cc15
-rw-r--r--chromium/weblayer/browser/weblayer_features.h19
-rw-r--r--chromium/weblayer/browser/weblayer_field_trials.cc21
-rw-r--r--chromium/weblayer/browser/weblayer_field_trials.h6
-rw-r--r--chromium/weblayer/browser/weblayer_impl_android.cc8
-rw-r--r--chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc22
-rw-r--r--chromium/weblayer/browser/weblayer_security_blocking_page_factory.h4
-rw-r--r--chromium/weblayer/browser/weblayer_site_settings_client.cc68
-rw-r--r--chromium/weblayer/browser/weblayer_speech_recognition_manager_delegate.cc9
273 files changed, 12327 insertions, 1531 deletions
diff --git a/chromium/weblayer/browser/DEPS b/chromium/weblayer/browser/DEPS
index 36478fd6316..d7477650b25 100644
--- a/chromium/weblayer/browser/DEPS
+++ b/chromium/weblayer/browser/DEPS
@@ -2,9 +2,11 @@ include_rules = [
"+cc",
"+components/autofill/android",
"+components/autofill/content/browser",
+ "+components/autofill/content/common",
"+components/autofill/core/browser",
"+components/autofill/core/common",
"+components/base32",
+ "+components/blocked_content",
"+components/browser_ui",
"+components/captive_portal",
"+components/cdm/browser",
@@ -17,26 +19,30 @@ include_rules = [
"+components/download/public/common",
"+components/embedder_support",
"+components/find_in_page",
+ "+components/infobars/content",
"+components/infobars/core",
"+components/javascript_dialogs",
"+components/keyed_service/content",
+ "+components/keyed_service/core",
"+components/language/core/browser",
"+components/metrics",
"+components/navigation_interception",
+ "+components/network_session_configurator",
"+components/network_time",
"+components/page_load_metrics/browser",
+ "+components/password_manager/content/browser",
"+components/permissions",
"+components/pref_registry",
"+components/prefs",
- "+components/url_formatter",
- "+components/user_prefs",
"+components/resources/android",
+ "+components/safe_browsing/core",
"+components/safe_browsing/core/common",
"+components/safe_browsing/core/features.h",
"+components/security_interstitials",
"+components/security_state/content/content_utils.h",
"+components/security_state/core/security_state.h",
"+components/sessions",
+ "+components/site_isolation",
"+components/spellcheck/browser",
"+components/ssl_errors",
"+components/startup_metric_utils",
@@ -44,6 +50,10 @@ include_rules = [
"+components/translate/content/browser",
"+components/translate/core/browser",
"+components/translate/core/common",
+ "+components/ukm",
+ "+components/unified_consent/pref_names.h",
+ "+components/url_formatter",
+ "+components/user_prefs",
"+components/variations",
"+components/version_info",
"+components/web_cache/browser",
diff --git a/chromium/weblayer/browser/android/javatests/BUILD.gn b/chromium/weblayer/browser/android/javatests/BUILD.gn
index 867ce21663e..d069ae4c405 100644
--- a/chromium/weblayer/browser/android/javatests/BUILD.gn
+++ b/chromium/weblayer/browser/android/javatests/BUILD.gn
@@ -9,7 +9,6 @@ import("//build/config/android/rules.gni")
android_library("weblayer_java_tests") {
testonly = true
sources = [
- "src/org/chromium/weblayer/test/BottomControlsTest.java",
"src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java",
"src/org/chromium/weblayer/test/CookieManagerTest.java",
"src/org/chromium/weblayer/test/CrashReporterTest.java",
@@ -17,18 +16,17 @@ android_library("weblayer_java_tests") {
"src/org/chromium/weblayer/test/DowngradeTest.java",
"src/org/chromium/weblayer/test/DownloadCallbackTest.java",
"src/org/chromium/weblayer/test/ErrorPageCallbackTest.java",
- "src/org/chromium/weblayer/test/EventUtils.java",
"src/org/chromium/weblayer/test/ExecuteScriptTest.java",
"src/org/chromium/weblayer/test/ExternalNavigationTest.java",
"src/org/chromium/weblayer/test/FindInPageTest.java",
"src/org/chromium/weblayer/test/FullscreenCallbackTest.java",
"src/org/chromium/weblayer/test/InputTypesTest.java",
"src/org/chromium/weblayer/test/NavigationTest.java",
- "src/org/chromium/weblayer/test/NewTabCallbackImpl.java",
"src/org/chromium/weblayer/test/NewTabCallbackTest.java",
"src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java",
"src/org/chromium/weblayer/test/ProfileTest.java",
"src/org/chromium/weblayer/test/RenderingTest.java",
+ "src/org/chromium/weblayer/test/SiteSettingsTest.java",
"src/org/chromium/weblayer/test/SmokeTest.java",
"src/org/chromium/weblayer/test/TabCallbackTest.java",
"src/org/chromium/weblayer/test/TabListCallbackTest.java",
@@ -37,6 +35,7 @@ android_library("weblayer_java_tests") {
"src/org/chromium/weblayer/test/TopControlsTest.java",
"src/org/chromium/weblayer/test/WebLayerLoadingTest.java",
"src/org/chromium/weblayer/test/WebLayerTest.java",
+ "src/org/chromium/weblayer/test/WebMessageTest.java",
"src/org/chromium/weblayer/test/WebViewCompatibilityTest.java",
]
deps = [
@@ -49,8 +48,10 @@ android_library("weblayer_java_tests") {
"//third_party/android_deps:android_support_v4_java",
"//third_party/android_deps:androidx_core_core_java",
"//third_party/android_deps:androidx_fragment_fragment_java",
+ "//third_party/android_deps:espresso_java",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//weblayer/browser/java:interfaces_java",
"//weblayer/public/java",
@@ -62,12 +63,17 @@ android_library("weblayer_java_tests") {
android_library("weblayer_private_java_tests") {
testonly = true
sources = [
+ "src/org/chromium/weblayer/test/BrowserControlsTest.java",
"src/org/chromium/weblayer/test/GeolocationTest.java",
+ "src/org/chromium/weblayer/test/InfoBarTest.java",
"src/org/chromium/weblayer/test/MediaCaptureTest.java",
"src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java",
+ "src/org/chromium/weblayer/test/PopupTest.java",
"src/org/chromium/weblayer/test/ResourceLoadingTest.java",
+ "src/org/chromium/weblayer/test/TranslateTest.java",
]
deps = [
+ ":weblayer_java_private_test_support",
":weblayer_java_test_support",
"//base:base_java",
"//base:base_java_test_support",
@@ -77,6 +83,7 @@ android_library("weblayer_private_java_tests") {
"//third_party/android_deps:androidx_fragment_fragment_java",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit:junit",
"//weblayer/public/java",
"//weblayer/public/javatestutil:test_java",
@@ -88,11 +95,15 @@ android_library("weblayer_java_test_support") {
testonly = true
sources = [
"src/org/chromium/weblayer/test/BoundedCountDownLatch.java",
+ "src/org/chromium/weblayer/test/EventUtils.java",
"src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java",
"src/org/chromium/weblayer/test/MinWebLayerVersion.java",
"src/org/chromium/weblayer/test/MinWebLayerVersionSkipCheck.java",
"src/org/chromium/weblayer/test/NavigationWaiter.java",
+ "src/org/chromium/weblayer/test/NewTabCallbackImpl.java",
"src/org/chromium/weblayer/test/ResourceUtil.java",
+ "src/org/chromium/weblayer/test/SiteSettingsActivityTestRule.java",
+ "src/org/chromium/weblayer/test/WebLayerActivityTestRule.java",
"src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java",
]
deps = [
@@ -101,8 +112,28 @@ android_library("weblayer_java_test_support") {
"//content/public/test/android:content_java_test_support",
"//net/android:net_java_test_support",
"//third_party/android_deps:androidx_core_core_java",
+ "//third_party/android_deps:androidx_fragment_fragment_java",
+ "//third_party/android_support_test_runner:rules_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/hamcrest:hamcrest_java",
+ "//third_party/junit:junit",
+ "//weblayer/public/java",
+ "//weblayer/shell/android:weblayer_shell_java",
+ ]
+}
+
+android_library("weblayer_java_private_test_support") {
+ testonly = true
+ sources = [ "src/org/chromium/weblayer/test/BrowserControlsHelper.java" ]
+ deps = [
+ ":weblayer_java_test_support",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//content/public/test/android:content_java_test_support",
"//third_party/hamcrest:hamcrest_java",
+ "//third_party/junit:junit",
"//weblayer/public/java",
+ "//weblayer/public/javatestutil:test_java",
"//weblayer/shell/android:weblayer_shell_java",
]
}
@@ -195,11 +226,15 @@ android_test_apk("weblayer_bundle_test_apk") {
]
deps = [
":weblayer_java_test_support",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//content/public/test/android:content_java_test_support",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/junit:junit",
"//weblayer/public/java",
"//weblayer/public/javatestutil:test_java",
+ "//weblayer/shell/android:weblayer_shell_java",
]
never_incremental = true
}
diff --git a/chromium/weblayer/browser/android/javatests/skew/expectations.txt b/chromium/weblayer/browser/android/javatests/skew/expectations.txt
index 56665be1d67..365f067e95e 100644
--- a/chromium/weblayer/browser/android/javatests/skew/expectations.txt
+++ b/chromium/weblayer/browser/android/javatests/skew/expectations.txt
@@ -4,16 +4,13 @@
# Lines tagged with "impl_lte_$VERSION" will be active when testing trunk client
# with versions less than or equal to $VERSION of the implementation.
#
-# tags: [ impl_lte_81 impl_lte_82 impl_lte_83 client_lte_83 ]
+# tags: [ impl_lte_83 client_lte_83 client_lte_84 ]
# results: [ Skip ]
# ---------------------------------------------
# Tests against older WebLayer implementations.
# ---------------------------------------------
-# Strict mode violation was fixed in M83 in https://crrev.com/c/2108603.
-[ impl_lte_82 ] org.chromium.weblayer.test.InputTypesTest#testColorInput [ Skip ]
-
# ExternalNavigationTests are testing intent launching that changed after M83.
[ impl_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentInNewTabLaunchedOnLinkClick [ Skip ]
[ impl_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentViaOnLoadBlocked [ Skip ]
@@ -31,57 +28,9 @@
# Fixed in https://crrev.com/c/2180022, see https://crbug.com/1077243.
[ impl_lte_83 ] org.chromium.weblayer.test.FullscreenCallbackTest#testExitFullscreenWhenTabDestroyed [ Skip ]
-# https://crbug.com/1079489.
-[ impl_lte_83 ] org.chromium.weblayer.test.BottomControlsTest#testBasic [ Skip ]
-[ impl_lte_83 ] org.chromium.weblayer.test.BottomControlsTest#testNoTopControl [ Skip ]
-
# https://crbug.com/1079491.
[ impl_lte_83 ] org.chromium.weblayer.test.NavigationTest#testSetUserAgentString [ Skip ]
-# DownloadCallback moved from Tab to Profile in M83: https://crrev.com/cc967e92032594c0e54d02e31824f92aff5f30cd
-[ impl_lte_82 ] org.chromium.weblayer.test.DownloadCallbackTest#testBasic [ Skip ]
-[ impl_lte_82 ] org.chromium.weblayer.test.DownloadCallbackTest#testInterceptDownloadByContentDisposition [ Skip ]
-[ impl_lte_82 ] org.chromium.weblayer.test.DownloadCallbackTest#testInterceptDownloadByLinkAttribute [ Skip ]
-
-# ExternalNavigationTest intent launching changed from using ApplicationContext
-# to Activity after M81.
-[ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentAfterRedirectLaunched [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentInSameTabLaunchedOnLinkClick [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentWithFallbackUrlAfterRedirectLaunched [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ExternalNavigationTest#testNonHandledExternalIntentWithFallbackUrlAfterRedirectGoesToFallbackUrl [ Skip ]
-
-# Many M81 tests are broken, see https://crbug.com/1081102.
-[ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoreAfterRecreate [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoreTabGuidAfterRecreate [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.BrowserFragmentLifecycleTest#restoresTabGuid [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testCookieChanged [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testCookieChangedRemoveCallback [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testCookieChangedRemoveCallbackAfterProfileDestroyed [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testGetCookie [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testSetCookie [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testSetCookieInvalid [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testSetCookieNotSet [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CookieManagerTest#testSetCookieNullCallback [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.CrashReporterTest#testCrashReporterLoading [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.DowngradeTest#testDowngradeDeletesData [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.InputTypesTest#testColorInput [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testRepostConfirmation [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderInRedirect [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderInStart [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderThrowsExceptionInCompleted [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testSetRequestHeaderThrowsExceptionWithInvalidValue [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.NavigationTest#testStopFromOnNavigationStarted [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testDestroyAndDeleteDataFromDisk [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testDestroyAndDeleteDataFromDiskIncognito [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testEnumerateAllProfileNames [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.ProfileTest#testReuseProfile [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testDismissTransientUi [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testOnTitleUpdated [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testShowContextMenu [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testShowContextMenuImg [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlay [ Skip ]
-[ impl_lte_81 ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlayOnBackgroundTab [ Skip ]
-
# -------------------------------------
# Tests against older WebLayer clients.
# -------------------------------------
@@ -92,3 +41,6 @@
# Replace was removed in https://crrev.com/c/2150968, see https://crbug.com/1070851.
[ client_lte_83 ] org.chromium.weblayer.test.NavigationTest#testReplace [ Skip ]
+
+# Test was made private, https://crbug.com/1087451.
+[ client_lte_84 ] org.chromium.weblayer.test.TopControlsTest#testBasic [ Skip ]
diff --git a/chromium/weblayer/browser/android/metrics/DEPS b/chromium/weblayer/browser/android/metrics/DEPS
index d9ef433896d..44a37a03920 100644
--- a/chromium/weblayer/browser/android/metrics/DEPS
+++ b/chromium/weblayer/browser/android/metrics/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+components/metrics",
+ "+google_apis/google_api_keys.h",
]
diff --git a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_accessor.h b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_accessor.h
deleted file mode 100644
index 173e5d51d13..00000000000
--- a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_accessor.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-#ifndef WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_ACCESSOR_H_
-#define WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_ACCESSOR_H_
-
-#include <stdint.h>
-#include <vector>
-
-#include "components/metrics/metrics_service_accessor.h"
-
-namespace weblayer {
-
-// This class limits and documents access to metrics service helper methods.
-// Since these methods are private, each user has to be explicitly declared
-// as a 'friend' below.
-class WebLayerMetricsServiceAccessor : public metrics::MetricsServiceAccessor {
- private:
- // For RegisterSyntheticMultiGroupFieldTrial.
- friend class WebLayerMetricsServiceClient;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(WebLayerMetricsServiceAccessor);
-};
-
-} // namespace weblayer
-
-#endif // WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_ACCESSOR_H_ \ No newline at end of file
diff --git a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc
index 4c879a069be..569fe1d44e2 100644
--- a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc
+++ b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc
@@ -8,14 +8,18 @@
#include <cstdint>
#include <memory>
+#include "base/base64.h"
#include "base/no_destructor.h"
-#include "base/strings/string_number_conversions.h"
+#include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_service.h"
-#include "components/variations/hashing.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
#include "components/version_info/android/channel_getter.h"
-#include "weblayer/browser/android/metrics/weblayer_metrics_service_accessor.h"
+#include "content/public/browser/browser_context.h"
+#include "google_apis/google_api_keys.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/java/jni/MetricsServiceClient_jni.h"
+#include "weblayer/browser/system_network_context_manager.h"
+#include "weblayer/browser/tab_impl.h"
namespace weblayer {
@@ -40,6 +44,28 @@ const int kBetaDevCanarySampledInRatePerMille = 990;
// consulting with the privacy team.
const int kPackageNameLimitRatePerMille = 100;
+// MetricsProvider that interfaces with page_load_metrics.
+class PageLoadMetricsProvider : public metrics::MetricsProvider {
+ public:
+ PageLoadMetricsProvider() = default;
+ ~PageLoadMetricsProvider() override = default;
+
+ // metrics:MetricsProvider implementation:
+ void OnAppEnterBackground() override {
+ auto tabs = TabImpl::GetAllTabImpl();
+ for (auto* tab : tabs) {
+ page_load_metrics::MetricsWebContentsObserver* observer =
+ page_load_metrics::MetricsWebContentsObserver::FromWebContents(
+ tab->web_contents());
+ if (observer)
+ observer->FlushMetricsOnAppEnterBackground();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PageLoadMetricsProvider);
+};
+
} // namespace
// static
@@ -49,47 +75,55 @@ WebLayerMetricsServiceClient* WebLayerMetricsServiceClient::GetInstance() {
return client.get();
}
-WebLayerMetricsServiceClient::WebLayerMetricsServiceClient() = default;
-WebLayerMetricsServiceClient::~WebLayerMetricsServiceClient() = default;
+WebLayerMetricsServiceClient::WebLayerMetricsServiceClient() {
+ ProfileImpl::AddProfileObserver(this);
+}
+
+WebLayerMetricsServiceClient::~WebLayerMetricsServiceClient() {
+ ProfileImpl::RemoveProfileObserver(this);
+}
-void WebLayerMetricsServiceClient::RegisterSyntheticMultiGroupFieldTrial(
- base::StringPiece trial_name,
+void WebLayerMetricsServiceClient::RegisterExternalExperiments(
const std::vector<int>& experiment_ids) {
if (!GetMetricsService()) {
if (!IsConsentDetermined()) {
post_start_tasks_.push_back(base::BindOnce(
- &WebLayerMetricsServiceClient::RegisterSyntheticMultiGroupFieldTrial,
- base::Unretained(this), trial_name, experiment_ids));
+ &WebLayerMetricsServiceClient::RegisterExternalExperiments,
+ base::Unretained(this), experiment_ids));
}
return;
}
- std::vector<uint32_t> group_name_hashes;
- group_name_hashes.reserve(experiment_ids.size());
-
- variations::ActiveGroupId active_group;
- active_group.name = variations::HashName(trial_name);
- for (int experiment_id : experiment_ids) {
- active_group.group =
- variations::HashName(base::NumberToString(experiment_id));
-
- // Since external experiments are not based on Chrome's low entropy source,
- // they are only sent to Google web properties for signed in users to make
- // sure that this couldn't be used to identify a user that's not signed in.
- variations::AssociateGoogleVariationIDForceHashes(
- variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN, active_group,
- static_cast<variations::VariationID>(experiment_id));
- group_name_hashes.push_back(active_group.group);
- }
-
- WebLayerMetricsServiceAccessor::RegisterSyntheticMultiGroupFieldTrial(
- GetMetricsService(), trial_name, group_name_hashes);
+ GetMetricsService()->synthetic_trial_registry()->RegisterExternalExperiments(
+ "WebLayerExperiments", experiment_ids,
+ variations::SyntheticTrialRegistry::kOverrideExistingIds);
}
int32_t WebLayerMetricsServiceClient::GetProduct() {
return metrics::ChromeUserMetricsExtension::ANDROID_WEBLAYER;
}
+bool WebLayerMetricsServiceClient::IsExternalExperimentAllowlistEnabled() {
+ // RegisterExternalExperiments() is actually used to register experiment ids
+ // coming from the app embedding WebLayer itself, rather than externally. So
+ // the allowlist shouldn't be applied.
+ return false;
+}
+
+bool WebLayerMetricsServiceClient::IsUkmAllowedForAllProfiles() {
+ for (auto* profile : ProfileImpl::GetAllProfiles()) {
+ if (!profile->GetBooleanSetting(SettingType::UKM_ENABLED))
+ return false;
+ }
+ return true;
+}
+
+std::string WebLayerMetricsServiceClient::GetUploadSigningKey() {
+ std::string decoded_key;
+ base::Base64Decode(google_apis::GetMetricsKey(), &decoded_key);
+ return decoded_key;
+}
+
int WebLayerMetricsServiceClient::GetSampleRatePerMille() {
version_info::Channel channel = version_info::android::GetChannel();
if (channel == version_info::Channel::STABLE ||
@@ -114,6 +148,38 @@ int WebLayerMetricsServiceClient::GetPackageNameLimitRatePerMille() {
return kPackageNameLimitRatePerMille;
}
+void WebLayerMetricsServiceClient::RegisterAdditionalMetricsProviders(
+ metrics::MetricsService* service) {
+ service->RegisterMetricsProvider(std::make_unique<PageLoadMetricsProvider>());
+}
+
+bool WebLayerMetricsServiceClient::EnablePersistentHistograms() {
+ return true;
+}
+
+bool WebLayerMetricsServiceClient::IsOffTheRecordSessionActive() {
+ for (auto* profile : ProfileImpl::GetAllProfiles()) {
+ if (profile->GetBrowserContext()->IsOffTheRecord())
+ return true;
+ }
+
+ return false;
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+WebLayerMetricsServiceClient::GetURLLoaderFactory() {
+ return SystemNetworkContextManager::GetInstance()
+ ->GetSharedURLLoaderFactory();
+}
+
+void WebLayerMetricsServiceClient::ProfileCreated(ProfileImpl* profile) {
+ UpdateUkmService();
+}
+
+void WebLayerMetricsServiceClient::ProfileDestroyed(ProfileImpl* profile) {
+ UpdateUkmService();
+}
+
// static
void JNI_MetricsServiceClient_SetHaveMetricsConsent(JNIEnv* env,
jboolean user_consent,
diff --git a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h
index cc07ae5a02c..a25c0516ceb 100644
--- a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h
+++ b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h
@@ -18,11 +18,13 @@
#include "components/metrics/metrics_service_client.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+#include "weblayer/browser/profile_impl.h"
namespace weblayer {
class WebLayerMetricsServiceClient
- : public ::metrics::AndroidMetricsServiceClient {
+ : public ::metrics::AndroidMetricsServiceClient,
+ public ProfileImpl::ProfileObserver {
friend class base::NoDestructor<WebLayerMetricsServiceClient>;
public:
@@ -31,20 +33,30 @@ class WebLayerMetricsServiceClient
WebLayerMetricsServiceClient();
~WebLayerMetricsServiceClient() override;
- void RegisterSyntheticMultiGroupFieldTrial(
- base::StringPiece trial_name,
- const std::vector<int>& experiment_ids);
+ void RegisterExternalExperiments(const std::vector<int>& experiment_ids);
// metrics::MetricsServiceClient
int32_t GetProduct() override;
+ bool IsExternalExperimentAllowlistEnabled() override;
+ bool IsUkmAllowedForAllProfiles() override;
+ std::string GetUploadSigningKey() override;
// metrics::AndroidMetricsServiceClient:
int GetSampleRatePerMille() override;
void OnMetricsStart() override;
void OnMetricsNotStarted() override;
int GetPackageNameLimitRatePerMille() override;
+ void RegisterAdditionalMetricsProviders(
+ metrics::MetricsService* service) override;
+ bool EnablePersistentHistograms() override;
+ bool IsOffTheRecordSessionActive() override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
private:
+ // ProfileImpl::ProfileObserver:
+ void ProfileCreated(ProfileImpl* profile) override;
+ void ProfileDestroyed(ProfileImpl* profile) override;
+
std::vector<base::OnceClosure> post_start_tasks_;
DISALLOW_COPY_AND_ASSIGN(WebLayerMetricsServiceClient);
diff --git a/chromium/weblayer/browser/android/resource_mapper.cc b/chromium/weblayer/browser/android/resource_mapper.cc
index 24862f4b20a..ae5bcdf81d4 100644
--- a/chromium/weblayer/browser/android/resource_mapper.cc
+++ b/chromium/weblayer/browser/android/resource_mapper.cc
@@ -38,6 +38,7 @@ void ConstructMap() {
(*GetIdMap())[c_id] = resource_id_list[next_id++];
#define DECLARE_RESOURCE_ID(c_id, java_id) \
(*GetIdMap())[c_id] = resource_id_list[next_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"
#undef LINK_RESOURCE_ID
diff --git a/chromium/weblayer/browser/autofill_client_impl.cc b/chromium/weblayer/browser/autofill_client_impl.cc
index a5b25e0e0fe..f049ffa513f 100644
--- a/chromium/weblayer/browser/autofill_client_impl.cc
+++ b/chromium/weblayer/browser/autofill_client_impl.cc
@@ -4,6 +4,7 @@
#include "weblayer/browser/autofill_client_impl.h"
+#include "components/autofill/core/browser/ui/suggestion.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
@@ -203,11 +204,7 @@ void AutofillClientImpl::ScanCreditCard(CreditCardScanCallback callback) {
}
void AutofillClientImpl::ShowAutofillPopup(
- const gfx::RectF& element_bounds,
- base::i18n::TextDirection text_direction,
- const std::vector<autofill::Suggestion>& suggestions,
- bool /*unused_autoselect_first_suggestion*/,
- autofill::PopupType popup_type,
+ const autofill::AutofillClient::PopupOpenArgs& open_args,
base::WeakPtr<autofill::AutofillPopupDelegate> delegate) {
NOTREACHED();
}
@@ -235,6 +232,12 @@ void AutofillClientImpl::PinPopupView() {
NOTIMPLEMENTED();
}
+autofill::AutofillClient::PopupOpenArgs AutofillClientImpl::GetReopenPopupArgs()
+ const {
+ NOTIMPLEMENTED();
+ return {};
+}
+
void AutofillClientImpl::UpdatePopup(
const std::vector<autofill::Suggestion>& suggestions,
autofill::PopupType popup_type) {
diff --git a/chromium/weblayer/browser/autofill_client_impl.h b/chromium/weblayer/browser/autofill_client_impl.h
index 4ff8339b28d..c5edc4a218a 100644
--- a/chromium/weblayer/browser/autofill_client_impl.h
+++ b/chromium/weblayer/browser/autofill_client_impl.h
@@ -97,17 +97,14 @@ class AutofillClientImpl
bool HasCreditCardScanFeature() override;
void ScanCreditCard(CreditCardScanCallback callback) override;
void ShowAutofillPopup(
- const gfx::RectF& element_bounds,
- base::i18n::TextDirection text_direction,
- const std::vector<autofill::Suggestion>& suggestions,
- bool /*unused_autoselect_first_suggestion*/,
- autofill::PopupType popup_type,
+ const autofill::AutofillClient::PopupOpenArgs& open_args,
base::WeakPtr<autofill::AutofillPopupDelegate> delegate) override;
void UpdateAutofillPopupDataListValues(
const std::vector<base::string16>& values,
const std::vector<base::string16>& labels) override;
base::span<const autofill::Suggestion> GetPopupSuggestions() const override;
void PinPopupView() override;
+ autofill::AutofillClient::PopupOpenArgs GetReopenPopupArgs() const override;
void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions,
autofill::PopupType popup_type) override;
void HideAutofillPopup(autofill::PopupHidingReason reason) override;
diff --git a/chromium/weblayer/browser/browser_context_impl.cc b/chromium/weblayer/browser/browser_context_impl.cc
index bd790631ab0..0e99612fdce 100644
--- a/chromium/weblayer/browser/browser_context_impl.cc
+++ b/chromium/weblayer/browser/browser_context_impl.cc
@@ -5,6 +5,7 @@
#include "weblayer/browser/browser_context_impl.h"
#include "base/threading/thread_restrictions.h"
+#include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
#include "components/client_hints/browser/client_hints.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/download/public/common/in_progress_download_manager.h"
@@ -19,7 +20,8 @@
#include "components/prefs/pref_service_factory.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
-#include "components/security_state/core/security_state.h"
+#include "components/site_isolation/pref_names.h"
+#include "components/site_isolation/site_isolation_policy.h"
#include "components/translate/core/browser/translate_pref_names.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "components/user_prefs/user_prefs.h"
@@ -29,6 +31,8 @@
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/storage_partition.h"
+#include "weblayer/browser/browsing_data_remover_delegate.h"
+#include "weblayer/browser/browsing_data_remover_delegate_factory.h"
#include "weblayer/browser/client_hints_factory.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
#include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h"
@@ -38,6 +42,7 @@
#include "base/android/path_utils.h"
#include "components/cdm/browser/media_drm_storage_impl.h" // nogncheck
#include "components/permissions/contexts/geolocation_permission_context_android.h"
+#include "components/unified_consent/pref_names.h"
#elif defined(OS_WIN)
#include <KnownFolders.h>
#include <shlobj.h>
@@ -63,6 +68,11 @@ void BindWakeLockProvider(
} // namespace
+namespace prefs {
+// Used to persist the public SettingType::UKM_ENABLED API.
+const char kUkmEnabled[] = "weblayer.ukm_enabled";
+} // namespace prefs
+
class ResourceContextImpl : public content::ResourceContext {
public:
ResourceContextImpl() = default;
@@ -82,6 +92,8 @@ BrowserContextImpl::BrowserContextImpl(ProfileImpl* profile_impl,
BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices(
this);
+
+ site_isolation::SiteIsolationPolicy::ApplyPersistedIsolatedOrigins(this);
}
BrowserContextImpl::~BrowserContextImpl() {
@@ -178,7 +190,7 @@ BrowserContextImpl::GetBackgroundSyncController() {
content::BrowsingDataRemoverDelegate*
BrowserContextImpl::GetBrowsingDataRemoverDelegate() {
- return nullptr;
+ return BrowsingDataRemoverDelegateFactory::GetForBrowserContext(this);
}
download::InProgressDownloadManager*
@@ -228,25 +240,32 @@ void BrowserContextImpl::CreateUserPrefService() {
void BrowserContextImpl::RegisterPrefs(
user_prefs::PrefRegistrySyncable* pref_registry) {
+ pref_registry->RegisterBooleanPref(prefs::kUkmEnabled, false);
+
// This pref is used by captive_portal::CaptivePortalService (as well as other
// potential use cases in the future, as it is used for various purposes
// through //chrome).
pref_registry->RegisterBooleanPref(
embedder_support::kAlternateErrorPagesEnabled, true);
+ pref_registry->RegisterListPref(
+ site_isolation::prefs::kUserTriggeredIsolatedOrigins);
StatefulSSLHostStateDelegate::RegisterProfilePrefs(pref_registry);
HostContentSettingsMap::RegisterProfilePrefs(pref_registry);
safe_browsing::RegisterProfilePrefs(pref_registry);
- security_state::RegisterProfilePrefs(pref_registry);
language::LanguagePrefs::RegisterProfilePrefs(pref_registry);
translate::TranslatePrefs::RegisterProfilePrefs(pref_registry);
+ blocked_content::SafeBrowsingTriggeredPopupBlocker::RegisterProfilePrefs(
+ pref_registry);
pref_registry->RegisterBooleanPref(
- prefs::kOfferTranslateEnabled, true,
+ ::prefs::kOfferTranslateEnabled, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
#if defined(OS_ANDROID)
cdm::MediaDrmStorageImpl::RegisterProfilePrefs(pref_registry);
permissions::GeolocationPermissionContextAndroid::RegisterProfilePrefs(
pref_registry);
+ pref_registry->RegisterBooleanPref(
+ unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
#endif
BrowserContextDependencyManager::GetInstance()
@@ -261,7 +280,7 @@ class BrowserContextImpl::WebLayerVariationsClient
~WebLayerVariationsClient() override = default;
- bool IsIncognito() const override {
+ bool IsOffTheRecord() const override {
return browser_context_->IsOffTheRecord();
}
diff --git a/chromium/weblayer/browser/browser_context_impl.h b/chromium/weblayer/browser/browser_context_impl.h
index 90d65f56290..7d888462025 100644
--- a/chromium/weblayer/browser/browser_context_impl.h
+++ b/chromium/weblayer/browser/browser_context_impl.h
@@ -21,6 +21,11 @@ namespace weblayer {
class ProfileImpl;
class ResourceContextImpl;
+namespace prefs {
+// WebLayer specific pref names.
+extern const char kUkmEnabled[];
+} // namespace prefs
+
class BrowserContextImpl : public content::BrowserContext {
public:
BrowserContextImpl(ProfileImpl* profile_impl, const base::FilePath& path);
diff --git a/chromium/weblayer/browser/browser_controls_container_view.cc b/chromium/weblayer/browser/browser_controls_container_view.cc
index aa090d4315b..81a1f9e9626 100644
--- a/chromium/weblayer/browser/browser_controls_container_view.cc
+++ b/chromium/weblayer/browser/browser_controls_container_view.cc
@@ -5,6 +5,8 @@
#include "weblayer/browser/browser_controls_container_view.h"
#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/feature_list.h"
#include "cc/layers/ui_resource_layer.h"
#include "content/public/browser/android/compositor.h"
#include "content/public/browser/render_view_host.h"
@@ -16,6 +18,7 @@
#include "ui/android/view_android.h"
#include "weblayer/browser/content_view_render_view.h"
#include "weblayer/browser/java/jni/BrowserControlsContainerView_jni.h"
+#include "weblayer/browser/weblayer_features.h"
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
@@ -31,9 +34,19 @@ BrowserControlsContainerView::BrowserControlsContainerView(
content_view_render_view_(content_view_render_view),
is_top_(is_top) {
DCHECK(content_view_render_view_);
+ if (!is_top_) {
+ content_view_render_view_->SetHeightChangedListener(
+ base::BindRepeating(&BrowserControlsContainerView::ContentHeightChanged,
+ base::Unretained(this)));
+ }
}
-BrowserControlsContainerView::~BrowserControlsContainerView() = default;
+BrowserControlsContainerView::~BrowserControlsContainerView() {
+ if (!is_top_) {
+ content_view_render_view_->SetHeightChangedListener(
+ base::RepeatingClosure());
+ }
+}
int BrowserControlsContainerView::GetControlsHeight() {
return controls_layer_ ? controls_layer_->bounds().height() : 0;
@@ -80,28 +93,20 @@ void BrowserControlsContainerView::DeleteControlsLayer(JNIEnv* env) {
}
void BrowserControlsContainerView::SetTopControlsOffset(JNIEnv* env,
- int controls_offset_y,
int content_offset_y) {
DCHECK(is_top_);
// |controls_layer_| may not be created if the controls view has 0 height.
if (controls_layer_)
- controls_layer_->SetPosition(gfx::PointF(0, controls_offset_y));
+ controls_layer_->SetPosition(gfx::PointF(0, GetControlsOffset()));
if (web_contents()) {
web_contents()->GetNativeView()->GetLayer()->SetPosition(
gfx::PointF(0, content_offset_y));
}
}
-void BrowserControlsContainerView::SetBottomControlsOffset(
- JNIEnv* env,
- int controls_offset_y) {
+void BrowserControlsContainerView::SetBottomControlsOffset(JNIEnv* env) {
DCHECK(!is_top_);
- // |controls_layer_| may not be created if the controls view has 0 height.
- if (controls_layer_) {
- controls_layer_->SetPosition(
- gfx::PointF(0, content_view_render_view_->height() -
- GetControlsHeight() + controls_offset_y));
- }
+ DoSetBottomControlsOffset();
}
void BrowserControlsContainerView::SetControlsSize(
@@ -110,6 +115,8 @@ void BrowserControlsContainerView::SetControlsSize(
int height) {
DCHECK(controls_layer_);
controls_layer_->SetBounds(gfx::Size(width, height));
+ // It's assumed the caller handles triggering SynchronizeVisualProperties()
+ // being called (this is done in java code).
}
void BrowserControlsContainerView::UpdateControlsResource(JNIEnv* env) {
@@ -139,6 +146,26 @@ void BrowserControlsContainerView::DidToggleFullscreenModeForTab(
entered_fullscreen);
}
+void BrowserControlsContainerView::ContentHeightChanged() {
+ DCHECK(!is_top_);
+ DoSetBottomControlsOffset();
+}
+
+int BrowserControlsContainerView::GetControlsOffset() {
+ return Java_BrowserControlsContainerView_getControlsOffset(
+ AttachCurrentThread(), java_browser_controls_container_view_);
+}
+
+void BrowserControlsContainerView::DoSetBottomControlsOffset() {
+ DCHECK(!is_top_);
+ // |controls_layer_| may not be created if the controls view has 0 height.
+ if (!controls_layer_)
+ return;
+ controls_layer_->SetPosition(
+ gfx::PointF(0, content_view_render_view_->height() - GetControlsHeight() +
+ GetControlsOffset()));
+}
+
static jlong
JNI_BrowserControlsContainerView_CreateBrowserControlsContainerView(
JNIEnv* env,
@@ -151,4 +178,9 @@ JNI_BrowserControlsContainerView_CreateBrowserControlsContainerView(
is_top));
}
+static jboolean JNI_BrowserControlsContainerView_ShouldDelayVisibilityChange(
+ JNIEnv* env) {
+ return !base::FeatureList::IsEnabled(kImmediatelyHideBrowserControlsForTest);
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/browser_controls_container_view.h b/chromium/weblayer/browser/browser_controls_container_view.h
index 883ea0c49b6..9231531499d 100644
--- a/chromium/weblayer/browser/browser_controls_container_view.h
+++ b/chromium/weblayer/browser/browser_controls_container_view.h
@@ -55,9 +55,8 @@ class BrowserControlsContainerView : public content::WebContentsObserver {
// Sets the offsets of the controls and content. See
// BrowserControlsContainerView's javadoc for details on this.
void SetTopControlsOffset(JNIEnv* env,
- int controls_offset_y,
int content_offset_y);
- void SetBottomControlsOffset(JNIEnv* env, int controls_offset_y);
+ void SetBottomControlsOffset(JNIEnv* env);
// Sets the size of |controls_layer_|.
void SetControlsSize(JNIEnv* env,
@@ -75,6 +74,12 @@ class BrowserControlsContainerView : public content::WebContentsObserver {
void DidToggleFullscreenModeForTab(bool entered_fullscreen,
bool will_cause_resize) override;
+ // Only used for bottom controls.
+ void ContentHeightChanged();
+
+ int GetControlsOffset();
+ void DoSetBottomControlsOffset();
+
base::android::ScopedJavaGlobalRef<jobject>
java_browser_controls_container_view_;
ContentViewRenderView* content_view_render_view_;
diff --git a/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc b/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc
index 139a6ab86b3..a049f38ca42 100644
--- a/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc
+++ b/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc
@@ -18,11 +18,10 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "weblayer/browser/browser_controls_navigation_state_handler_delegate.h"
+#include "weblayer/browser/weblayer_features.h"
namespace weblayer {
namespace {
-const base::Feature kImmediatelyHideBrowserControlsForTest{
- "ImmediatelyHideBrowserControlsForTest", base::FEATURE_DISABLED_BY_DEFAULT};
// The time that must elapse after a navigation before the browser controls can
// be hidden. This value matches what chrome has in
@@ -92,14 +91,6 @@ void BrowserControlsNavigationStateHandler::DidChangeVisibleSecurityState() {
UpdateState();
}
-void BrowserControlsNavigationStateHandler::DidAttachInterstitialPage() {
- UpdateState();
-}
-
-void BrowserControlsNavigationStateHandler::DidDetachInterstitialPage() {
- UpdateState();
-}
-
void BrowserControlsNavigationStateHandler::RenderProcessGone(
base::TerminationStatus status) {
is_crashed_ = true;
@@ -153,7 +144,6 @@ BrowserControlsNavigationStateHandler::CalculateCurrentState() {
if (force_show_during_load_ || web_contents()->IsFullscreen() ||
web_contents()->IsFocusedElementEditable() ||
- web_contents()->ShowingInterstitialPage() ||
web_contents()->IsBeingDestroyed() || web_contents()->IsCrashed()) {
return content::BROWSER_CONTROLS_STATE_SHOWN;
}
@@ -176,7 +166,6 @@ BrowserControlsNavigationStateHandler::CalculateCurrentState() {
return content::BROWSER_CONTROLS_STATE_SHOWN;
case security_state::NONE:
- case security_state::EV_SECURE:
case security_state::SECURE:
case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
case security_state::SECURITY_LEVEL_COUNT:
diff --git a/chromium/weblayer/browser/browser_controls_navigation_state_handler.h b/chromium/weblayer/browser/browser_controls_navigation_state_handler.h
index aa7d27dafe7..0ad3b319b3e 100644
--- a/chromium/weblayer/browser/browser_controls_navigation_state_handler.h
+++ b/chromium/weblayer/browser/browser_controls_navigation_state_handler.h
@@ -48,8 +48,6 @@ class BrowserControlsNavigationStateHandler
const GURL& validated_url,
int error_code) override;
void DidChangeVisibleSecurityState() override;
- void DidAttachInterstitialPage() override;
- void DidDetachInterstitialPage() override;
void RenderProcessGone(base::TerminationStatus status) override;
void OnRendererUnresponsive(
content::RenderProcessHost* render_process_host) override;
diff --git a/chromium/weblayer/browser/browser_impl.cc b/chromium/weblayer/browser/browser_impl.cc
index d9344a7b4e6..b7f5dc490b3 100644
--- a/chromium/weblayer/browser/browser_impl.cc
+++ b/chromium/weblayer/browser/browser_impl.cc
@@ -10,11 +10,14 @@
#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
+#include "base/stl_util.h"
#include "components/base32/base32.h"
-#include "content/public/browser/browser_context.h"
#include "content/public/common/web_preferences.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_list.h"
#include "weblayer/browser/feature_list_creator.h"
#include "weblayer/browser/persistence/browser_persister.h"
+#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/persistence/minimal_browser_persister.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
@@ -26,6 +29,9 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/json/json_writer.h"
+#include "components/metrics/metrics_service.h"
+#include "components/ukm/ukm_service.h"
+#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/java/jni/BrowserImpl_jni.h"
#endif
@@ -38,8 +44,43 @@ using base::android::ScopedJavaLocalRef;
namespace weblayer {
-// TODO(timvolodine): consider using an observer for this, crbug.com/1068713.
-int BrowserImpl::browser_count_ = 0;
+namespace {
+
+#if defined(OS_ANDROID)
+void UpdateMetricsService() {
+ static bool s_foreground = false;
+ // TODO(sky): convert this to observer.
+ bool foreground = BrowserList::GetInstance()->HasAtLeastOneResumedBrowser();
+
+ if (foreground == s_foreground)
+ return;
+
+ s_foreground = foreground;
+
+ auto* metrics_service =
+ WebLayerMetricsServiceClient::GetInstance()->GetMetricsService();
+ if (metrics_service) {
+ if (foreground)
+ metrics_service->OnAppEnterForeground();
+ else
+ metrics_service->OnAppEnterBackground();
+ }
+
+ auto* ukm_service =
+ WebLayerMetricsServiceClient::GetInstance()->GetUkmService();
+ if (ukm_service) {
+ if (foreground)
+ ukm_service->OnAppEnterForeground();
+ else
+ ukm_service->OnAppEnterBackground();
+ }
+}
+#endif // defined(OS_ANDROID)
+
+} // namespace
+
+// static
+constexpr char BrowserImpl::kPersistenceFilePrefix[];
std::unique_ptr<Browser> Browser::Create(
Profile* profile,
@@ -60,16 +101,13 @@ BrowserImpl::~BrowserImpl() {
DCHECK(tabs_.empty());
#else
while (!tabs_.empty())
- RemoveTab(tabs_.back().get());
+ DestroyTab(tabs_.back().get());
#endif
- profile_->DecrementBrowserImplCount();
- browser_count_--;
- DCHECK(browser_count_ >= 0);
+ BrowserList::GetInstance()->RemoveBrowser(this);
#if defined(OS_ANDROID)
- if (browser_count_ == 0) {
+ if (BrowserList::GetInstance()->browsers().empty())
BrowserProcess::GetInstance()->StopSafeBrowsingService();
- }
#endif
}
@@ -79,12 +117,15 @@ TabImpl* BrowserImpl::CreateTabForSessionRestore(
std::unique_ptr<TabImpl> tab =
std::make_unique<TabImpl>(profile_, std::move(web_contents), guid);
#if defined(OS_ANDROID)
- Java_BrowserImpl_createTabForSessionRestore(
+ Java_BrowserImpl_createJavaTabForNativeTab(
AttachCurrentThread(), java_impl_, reinterpret_cast<jlong>(tab.get()));
#endif
- TabImpl* tab_ptr = tab.get();
- AddTab(std::move(tab));
- return tab_ptr;
+ return AddTab(std::move(tab));
+}
+
+TabImpl* BrowserImpl::CreateTab(
+ std::unique_ptr<content::WebContents> web_contents) {
+ return CreateTabForSessionRestore(std::move(web_contents), std::string());
}
#if defined(OS_ANDROID)
@@ -95,13 +136,7 @@ bool BrowserImpl::CompositorHasSurface() {
void BrowserImpl::AddTab(JNIEnv* env,
long native_tab) {
- TabImpl* tab = reinterpret_cast<TabImpl*>(native_tab);
- std::unique_ptr<Tab> owned_tab;
- if (tab->browser())
- owned_tab = tab->browser()->RemoveTab(tab);
- else
- owned_tab.reset(tab);
- AddTab(std::move(owned_tab));
+ AddTab(reinterpret_cast<TabImpl*>(native_tab));
}
void BrowserImpl::RemoveTab(JNIEnv* env,
@@ -158,8 +193,7 @@ ScopedJavaLocalRef<jbyteArray> BrowserImpl::GetBrowserPersisterCryptoKey(
ScopedJavaLocalRef<jbyteArray> BrowserImpl::GetMinimalPersistenceState(
JNIEnv* env) {
- auto state = GetMinimalPersistenceState();
- return base::android::ToJavaByteArray(env, &(state.front()), state.size());
+ return base::android::ToJavaByteArray(env, GetMinimalPersistenceState());
}
void BrowserImpl::RestoreStateIfNecessary(
@@ -203,6 +237,14 @@ void BrowserImpl::OnFragmentStart(JNIEnv* env) {
FeatureListCreator::GetInstance()->OnBrowserFragmentStarted();
}
+void BrowserImpl::OnFragmentResume(JNIEnv* env) {
+ UpdateFragmentResumedState(true);
+}
+
+void BrowserImpl::OnFragmentPause(JNIEnv* env) {
+ UpdateFragmentResumedState(false);
+}
+
#endif
std::vector<uint8_t> BrowserImpl::GetMinimalPersistenceState(
@@ -223,41 +265,19 @@ void BrowserImpl::SetWebPreferences(content::WebPreferences* prefs) {
#endif
}
-Tab* BrowserImpl::AddTab(std::unique_ptr<Tab> tab) {
+void BrowserImpl::AddTab(Tab* tab) {
DCHECK(tab);
- TabImpl* tab_impl = static_cast<TabImpl*>(tab.get());
- DCHECK(!tab_impl->browser());
- tabs_.push_back(std::move(tab));
- tab_impl->set_browser(this);
-#if defined(OS_ANDROID)
- Java_BrowserImpl_onTabAdded(AttachCurrentThread(), java_impl_,
- tab_impl->GetJavaTab());
-#endif
- for (BrowserObserver& obs : browser_observers_)
- obs.OnTabAdded(tab_impl);
- return tab_impl;
-}
-
-std::unique_ptr<Tab> BrowserImpl::RemoveTab(Tab* tab) {
TabImpl* tab_impl = static_cast<TabImpl*>(tab);
- DCHECK_EQ(this, tab_impl->browser());
- static_cast<TabImpl*>(tab)->set_browser(nullptr);
- auto iter =
- std::find_if(tabs_.begin(), tabs_.end(), base::MatchesUniquePtr(tab));
- DCHECK(iter != tabs_.end());
- std::unique_ptr<Tab> owned_tab = std::move(*iter);
- tabs_.erase(iter);
- const bool active_tab_changed = active_tab_ == tab;
- if (active_tab_changed)
- SetActiveTab(nullptr);
+ std::unique_ptr<Tab> owned_tab;
+ if (tab_impl->browser())
+ owned_tab = tab_impl->browser()->RemoveTab(tab_impl);
+ else
+ owned_tab.reset(tab_impl);
+ AddTab(std::move(owned_tab));
+}
-#if defined(OS_ANDROID)
- Java_BrowserImpl_onTabRemoved(AttachCurrentThread(), java_impl_,
- tab ? tab_impl->GetJavaTab() : nullptr);
-#endif
- for (BrowserObserver& obs : browser_observers_)
- obs.OnTabRemoved(tab, active_tab_changed);
- return owned_tab;
+void BrowserImpl::DestroyTab(Tab* tab) {
+ RemoveTab(tab);
}
void BrowserImpl::SetActiveTab(Tab* tab) {
@@ -291,6 +311,10 @@ std::vector<Tab*> BrowserImpl::GetTabs() {
return tabs;
}
+Tab* BrowserImpl::CreateTab() {
+ return CreateTab(nullptr);
+}
+
void BrowserImpl::PrepareForShutdown() {
browser_persister_.reset();
}
@@ -312,9 +336,18 @@ void BrowserImpl::RemoveObserver(BrowserObserver* observer) {
browser_observers_.RemoveObserver(observer);
}
+void BrowserImpl::VisibleSecurityStateOfActiveTabChanged() {
+ if (visible_security_state_changed_callback_for_tests_)
+ std::move(visible_security_state_changed_callback_for_tests_).Run();
+
+#if defined(OS_ANDROID)
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_BrowserImpl_onVisibleSecurityStateOfActiveTabChanged(env, java_impl_);
+#endif
+}
+
BrowserImpl::BrowserImpl(ProfileImpl* profile) : profile_(profile) {
- profile_->IncrementBrowserImplCount();
- browser_count_++;
+ BrowserList::GetInstance()->AddBrowser(this);
}
void BrowserImpl::RestoreStateIfNecessary(
@@ -328,24 +361,59 @@ void BrowserImpl::RestoreStateIfNecessary(
}
}
-void BrowserImpl::VisibleSecurityStateOfActiveTabChanged() {
- if (visible_security_state_changed_callback_for_tests_)
- std::move(visible_security_state_changed_callback_for_tests_).Run();
+TabImpl* BrowserImpl::AddTab(std::unique_ptr<Tab> tab) {
+ TabImpl* tab_impl = static_cast<TabImpl*>(tab.get());
+ DCHECK(!tab_impl->browser());
+ tabs_.push_back(std::move(tab));
+ tab_impl->set_browser(this);
+#if defined(OS_ANDROID)
+ Java_BrowserImpl_onTabAdded(AttachCurrentThread(), java_impl_,
+ tab_impl->GetJavaTab());
+#endif
+ for (BrowserObserver& obs : browser_observers_)
+ obs.OnTabAdded(tab_impl);
+ return tab_impl;
+}
+
+std::unique_ptr<Tab> BrowserImpl::RemoveTab(Tab* tab) {
+ TabImpl* tab_impl = static_cast<TabImpl*>(tab);
+ DCHECK_EQ(this, tab_impl->browser());
+ static_cast<TabImpl*>(tab)->set_browser(nullptr);
+ auto iter =
+ std::find_if(tabs_.begin(), tabs_.end(), base::MatchesUniquePtr(tab));
+ DCHECK(iter != tabs_.end());
+ std::unique_ptr<Tab> owned_tab = std::move(*iter);
+ tabs_.erase(iter);
+ const bool active_tab_changed = active_tab_ == tab;
+ if (active_tab_changed)
+ SetActiveTab(nullptr);
#if defined(OS_ANDROID)
- JNIEnv* env = base::android::AttachCurrentThread();
- Java_BrowserImpl_onVisibleSecurityStateOfActiveTabChanged(env, java_impl_);
+ Java_BrowserImpl_onTabRemoved(AttachCurrentThread(), java_impl_,
+ tab ? tab_impl->GetJavaTab() : nullptr);
#endif
+ for (BrowserObserver& obs : browser_observers_)
+ obs.OnTabRemoved(tab, active_tab_changed);
+ return owned_tab;
}
base::FilePath BrowserImpl::GetBrowserPersisterDataPath() {
- base::FilePath base_path = profile_->GetBrowserPersisterDataBaseDir();
- DCHECK(!GetPersistenceId().empty());
- const std::string encoded_name = base32::Base32Encode(GetPersistenceId());
- return base_path.AppendASCII("State" + encoded_name);
+ return BuildPathForBrowserPersister(
+ profile_->GetBrowserPersisterDataBaseDir(), GetPersistenceId());
}
#if defined(OS_ANDROID)
+void BrowserImpl::UpdateFragmentResumedState(bool state) {
+ const bool old_has_at_least_one_active_browser =
+ BrowserList::GetInstance()->HasAtLeastOneResumedBrowser();
+ fragment_resumed_ = state;
+ UpdateMetricsService();
+ if (old_has_at_least_one_active_browser !=
+ BrowserList::GetInstance()->HasAtLeastOneResumedBrowser()) {
+ BrowserList::GetInstance()->NotifyHasAtLeastOneResumedBrowserChanged();
+ }
+}
+
// This function is friended. JNI_BrowserImpl_CreateBrowser can not be
// friended, as it requires browser_impl.h to include BrowserImpl_jni.h, which
// is problematic (meaning not really supported and generates compile errors).
diff --git a/chromium/weblayer/browser/browser_impl.h b/chromium/weblayer/browser/browser_impl.h
index b625c089b03..f5487af1cbc 100644
--- a/chromium/weblayer/browser/browser_impl.h
+++ b/chromium/weblayer/browser/browser_impl.h
@@ -34,6 +34,9 @@ class TabImpl;
class BrowserImpl : public Browser {
public:
+ // Prefix used for storing persistence state.
+ static constexpr char kPersistenceFilePrefix[] = "State";
+
BrowserImpl(const BrowserImpl&) = delete;
BrowserImpl& operator=(const BrowserImpl&) = delete;
~BrowserImpl() override;
@@ -47,6 +50,7 @@ class BrowserImpl : public Browser {
TabImpl* CreateTabForSessionRestore(
std::unique_ptr<content::WebContents> web_contents,
const std::string& guid);
+ TabImpl* CreateTab(std::unique_ptr<content::WebContents> web_contents);
#if defined(OS_ANDROID)
bool CompositorHasSurface();
@@ -74,6 +78,10 @@ class BrowserImpl : public Browser {
j_minimal_persistence_state);
void WebPreferencesChanged(JNIEnv* env);
void OnFragmentStart(JNIEnv* env);
+ void OnFragmentResume(JNIEnv* env);
+ void OnFragmentPause(JNIEnv* env);
+
+ bool fragment_resumed() { return fragment_resumed_; }
#endif
// Used in tests to specify a non-default max (0 means use the default).
@@ -90,11 +98,12 @@ class BrowserImpl : public Browser {
void SetWebPreferences(content::WebPreferences* prefs);
// Browser:
- Tab* AddTab(std::unique_ptr<Tab> tab) override;
- std::unique_ptr<Tab> RemoveTab(Tab* tab) override;
+ void AddTab(Tab* tab) override;
+ void DestroyTab(Tab* tab) override;
void SetActiveTab(Tab* tab) override;
Tab* GetActiveTab() override;
std::vector<Tab*> GetTabs() override;
+ Tab* CreateTab() override;
void PrepareForShutdown() override;
std::string GetPersistenceId() override;
std::vector<uint8_t> GetMinimalPersistenceState() override;
@@ -116,10 +125,16 @@ class BrowserImpl : public Browser {
void RestoreStateIfNecessary(const PersistenceInfo& persistence_info);
+ TabImpl* AddTab(std::unique_ptr<Tab> tab);
+ std::unique_ptr<Tab> RemoveTab(Tab* tab);
+
// Returns the path used by |browser_persister_|.
base::FilePath GetBrowserPersisterDataPath();
#if defined(OS_ANDROID)
+ void UpdateFragmentResumedState(bool state);
+
+ bool fragment_resumed_ = false;
base::android::ScopedJavaGlobalRef<jobject> java_impl_;
#endif
base::ObserverList<BrowserObserver> browser_observers_;
@@ -129,7 +144,6 @@ class BrowserImpl : public Browser {
std::string persistence_id_;
std::unique_ptr<BrowserPersister> browser_persister_;
base::OnceClosure visible_security_state_changed_callback_for_tests_;
- static int browser_count_;
};
} // namespace weblayer
diff --git a/chromium/weblayer/browser/browser_list.cc b/chromium/weblayer/browser/browser_list.cc
new file mode 100644
index 00000000000..baff432c616
--- /dev/null
+++ b/chromium/weblayer/browser/browser_list.cc
@@ -0,0 +1,66 @@
+// 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.
+
+#include "weblayer/browser/browser_list.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/browser_list_observer.h"
+
+namespace weblayer {
+
+// static
+BrowserList* BrowserList::GetInstance() {
+ static base::NoDestructor<BrowserList> browser_list;
+ return browser_list.get();
+}
+
+#if defined(OS_ANDROID)
+bool BrowserList::HasAtLeastOneResumedBrowser() {
+ return std::any_of(browsers_.begin(), browsers_.end(),
+ std::mem_fn(&BrowserImpl::fragment_resumed));
+}
+#endif
+
+void BrowserList::AddObserver(BrowserListObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void BrowserList::RemoveObserver(BrowserListObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+BrowserList::BrowserList() = default;
+
+BrowserList::~BrowserList() = default;
+
+void BrowserList::AddBrowser(BrowserImpl* browser) {
+ DCHECK(!browsers_.contains(browser));
+#if defined(OS_ANDROID)
+ // Browsers should not start out resumed.
+ DCHECK(!browser->fragment_resumed());
+#endif
+ browsers_.insert(browser);
+}
+
+void BrowserList::RemoveBrowser(BrowserImpl* browser) {
+ DCHECK(browsers_.contains(browser));
+#if defined(OS_ANDROID)
+ // Browsers should not be resumed when being destroyed.
+ DCHECK(!browser->fragment_resumed());
+#endif
+ browsers_.erase(browser);
+}
+
+#if defined(OS_ANDROID)
+void BrowserList::NotifyHasAtLeastOneResumedBrowserChanged() {
+ const bool value = HasAtLeastOneResumedBrowser();
+ for (BrowserListObserver& observer : observers_)
+ observer.OnHasAtLeastOneResumedBrowserStateChanged(value);
+}
+#endif
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/browser_list.h b/chromium/weblayer/browser/browser_list.h
new file mode 100644
index 00000000000..6852761d31a
--- /dev/null
+++ b/chromium/weblayer/browser/browser_list.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_BROWSER_LIST_H_
+#define WEBLAYER_BROWSER_BROWSER_LIST_H_
+
+#include "base/containers/flat_set.h"
+#include "base/no_destructor.h"
+#include "base/observer_list.h"
+#include "build/build_config.h"
+
+namespace weblayer {
+
+class BrowserImpl;
+class BrowserListObserver;
+
+// Tracks the set of browsers.
+class BrowserList {
+ public:
+ BrowserList(const BrowserList&) = delete;
+ BrowserList& operator=(const BrowserList&) = delete;
+
+ static BrowserList* GetInstance();
+
+ const base::flat_set<BrowserImpl*>& browsers() { return browsers_; }
+
+#if defined(OS_ANDROID)
+ // Returns true if there is at least one Browser in a resumed state.
+ bool HasAtLeastOneResumedBrowser();
+#endif
+
+ void AddObserver(BrowserListObserver* observer);
+ void RemoveObserver(BrowserListObserver* observer);
+
+ private:
+ friend class BrowserImpl;
+ friend class base::NoDestructor<BrowserList>;
+
+ BrowserList();
+ ~BrowserList();
+
+ void AddBrowser(BrowserImpl* browser);
+ void RemoveBrowser(BrowserImpl* browser);
+
+#if defined(OS_ANDROID)
+ void NotifyHasAtLeastOneResumedBrowserChanged();
+#endif
+
+ base::flat_set<BrowserImpl*> browsers_;
+ base::ObserverList<BrowserListObserver> observers_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_BROWSER_LIST_H_
diff --git a/chromium/weblayer/browser/browser_list_observer.h b/chromium/weblayer/browser/browser_list_observer.h
new file mode 100644
index 00000000000..2175937226e
--- /dev/null
+++ b/chromium/weblayer/browser/browser_list_observer.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_BROWSER_LIST_OBSERVER_H_
+#define WEBLAYER_BROWSER_BROWSER_LIST_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+#include "build/build_config.h"
+
+namespace weblayer {
+
+class BrowserListObserver : public base::CheckedObserver {
+ public:
+#if defined(OS_ANDROID)
+ // Called when the value of BrowserList::HasAtLeastOneResumedBrowser()
+ // changes.
+ void OnHasAtLeastOneResumedBrowserStateChanged(bool new_value) {}
+#endif
+
+ protected:
+ ~BrowserListObserver() override = default;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_BROWSER_LIST_OBSERVER_H_
diff --git a/chromium/weblayer/browser/browser_main_parts_impl.cc b/chromium/weblayer/browser/browser_main_parts_impl.cc
index f0672179029..7f9e8467a13 100644
--- a/chromium/weblayer/browser/browser_main_parts_impl.cc
+++ b/chromium/weblayer/browser/browser_main_parts_impl.cc
@@ -18,7 +18,6 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/url_constants.h"
#include "services/service_manager/embedder/result_codes.h"
@@ -36,15 +35,19 @@
#include "weblayer/public/main.h"
#if defined(OS_ANDROID)
+#include "base/command_line.h"
#include "components/crash/content/browser/child_exit_observer_android.h"
#include "components/crash/content/browser/child_process_crash_observer_android.h"
#include "components/crash/core/common/crash_key.h"
#include "components/javascript_dialogs/android/app_modal_dialog_view_android.h" // nogncheck
#include "components/javascript_dialogs/app_modal_dialog_manager.h" // nogncheck
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
#include "net/android/network_change_notifier_factory_android.h"
#include "net/base/network_change_notifier.h"
#include "weblayer/browser/android/metrics/uma_utils.h"
+#include "weblayer/browser/java/jni/MojoInterfaceRegistrar_jni.h"
+#include "weblayer/browser/weblayer_factory_impl_android.h"
#endif
#if defined(USE_X11)
@@ -111,6 +114,15 @@ int BrowserMainPartsImpl::PreCreateThreads() {
std::make_unique<crash_reporter::ChildProcessCrashObserver>());
crash_reporter::InitializeCrashKeys();
+
+ // MediaSession was implemented in M85, and requires both implementation and
+ // client libraries to be at least that new. The version check has to be in
+ // the browser process, but the command line flag is automatically propagated
+ // to renderers.
+ if (WebLayerFactoryImplAndroid::GetClientMajorVersion() < 85) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ ::switches::kDisableMediaSessionAPI);
+ }
#endif
return service_manager::RESULT_CODE_NORMAL_EXIT;
@@ -191,6 +203,9 @@ void BrowserMainPartsImpl::PreMainMessageLoopRun() {
base::android::AttachCurrentThread(), controller,
controller->web_contents()->GetTopLevelNativeWindow());
}));
+
+ Java_MojoInterfaceRegistrar_registerMojoInterfaces(
+ base::android::AttachCurrentThread());
#endif
}
diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate.cc b/chromium/weblayer/browser/browsing_data_remover_delegate.cc
new file mode 100644
index 00000000000..2a466fe8cad
--- /dev/null
+++ b/chromium/weblayer/browser/browsing_data_remover_delegate.cc
@@ -0,0 +1,54 @@
+// 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.
+
+#include "weblayer/browser/browsing_data_remover_delegate.h"
+
+#include "base/callback.h"
+#include "components/prefs/pref_service.h"
+#include "components/site_isolation//pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+namespace weblayer {
+
+BrowsingDataRemoverDelegate::BrowsingDataRemoverDelegate(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context) {}
+
+BrowsingDataRemoverDelegate::EmbedderOriginTypeMatcher
+BrowsingDataRemoverDelegate::GetOriginTypeMatcher() {
+ return EmbedderOriginTypeMatcher();
+}
+
+bool BrowsingDataRemoverDelegate::MayRemoveDownloadHistory() {
+ return true;
+}
+
+std::vector<std::string>
+BrowsingDataRemoverDelegate::GetDomainsForDeferredCookieDeletion(
+ uint64_t remove_mask) {
+ return {};
+}
+
+void BrowsingDataRemoverDelegate::RemoveEmbedderData(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ uint64_t remove_mask,
+ content::BrowsingDataFilterBuilder* filter_builder,
+ uint64_t origin_type_mask,
+ base::OnceClosure callback) {
+ // Note: if history is ever added to WebLayer, also remove isolated origins
+ // when history is cleared.
+ if (remove_mask & DATA_TYPE_ISOLATED_ORIGINS) {
+ user_prefs::UserPrefs::Get(browser_context_)
+ ->ClearPref(site_isolation::prefs::kUserTriggeredIsolatedOrigins);
+ // Note that this does not clear these sites from the in-memory map in
+ // ChildProcessSecurityPolicy, since that is not supported at runtime. That
+ // list of isolated sites is not directly exposed to users, though, and
+ // will be cleared on next restart.
+ }
+ std::move(callback).Run();
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate.h b/chromium/weblayer/browser/browsing_data_remover_delegate.h
new file mode 100644
index 00000000000..c2a996debba
--- /dev/null
+++ b/chromium/weblayer/browser/browsing_data_remover_delegate.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_
+#define WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/browsing_data_remover_delegate.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace weblayer {
+
+class BrowsingDataRemoverDelegate : public content::BrowsingDataRemoverDelegate,
+ public KeyedService {
+ public:
+ // This is an extension of content::BrowsingDataRemover::RemoveDataMask which
+ // includes all datatypes therefrom and adds additional WebLayer-specific
+ // ones.
+ enum DataType : uint64_t {
+ // Embedder can start adding datatypes after the last platform datatype.
+ DATA_TYPE_EMBEDDER_BEGIN =
+ content::BrowsingDataRemover::DATA_TYPE_CONTENT_END << 1,
+
+ // WebLayer-specific datatypes.
+ DATA_TYPE_ISOLATED_ORIGINS = DATA_TYPE_EMBEDDER_BEGIN,
+ };
+
+ explicit BrowsingDataRemoverDelegate(
+ content::BrowserContext* browser_context);
+
+ BrowsingDataRemoverDelegate(const BrowsingDataRemoverDelegate&) = delete;
+ BrowsingDataRemoverDelegate& operator=(const BrowsingDataRemoverDelegate&) =
+ delete;
+
+ // content::BrowsingDataRemoverDelegate:
+ EmbedderOriginTypeMatcher GetOriginTypeMatcher() override;
+ bool MayRemoveDownloadHistory() override;
+ std::vector<std::string> GetDomainsForDeferredCookieDeletion(
+ uint64_t remove_mask) override;
+ void RemoveEmbedderData(const base::Time& delete_begin,
+ const base::Time& delete_end,
+ uint64_t remove_mask,
+ content::BrowsingDataFilterBuilder* filter_builder,
+ uint64_t origin_type_mask,
+ base::OnceClosure callback) override;
+
+ private:
+ content::BrowserContext* browser_context_ = nullptr;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_
diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate_factory.cc b/chromium/weblayer/browser/browsing_data_remover_delegate_factory.cc
new file mode 100644
index 00000000000..acc4bcc9da0
--- /dev/null
+++ b/chromium/weblayer/browser/browsing_data_remover_delegate_factory.cc
@@ -0,0 +1,46 @@
+// 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.
+
+#include "weblayer/browser/browsing_data_remover_delegate_factory.h"
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "weblayer/browser/browsing_data_remover_delegate.h"
+
+namespace weblayer {
+
+// static
+BrowsingDataRemoverDelegate*
+BrowsingDataRemoverDelegateFactory::GetForBrowserContext(
+ content::BrowserContext* browser_context) {
+ return static_cast<BrowsingDataRemoverDelegate*>(
+ GetInstance()->GetServiceForBrowserContext(browser_context, true));
+}
+
+// static
+BrowsingDataRemoverDelegateFactory*
+BrowsingDataRemoverDelegateFactory::GetInstance() {
+ static base::NoDestructor<BrowsingDataRemoverDelegateFactory> factory;
+ return factory.get();
+}
+
+BrowsingDataRemoverDelegateFactory::BrowsingDataRemoverDelegateFactory()
+ : BrowserContextKeyedServiceFactory(
+ "BrowsingDataRemoverDelegate",
+ BrowserContextDependencyManager::GetInstance()) {}
+
+BrowsingDataRemoverDelegateFactory::~BrowsingDataRemoverDelegateFactory() =
+ default;
+
+KeyedService* BrowsingDataRemoverDelegateFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ return new BrowsingDataRemoverDelegate(context);
+}
+
+content::BrowserContext*
+BrowsingDataRemoverDelegateFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return context;
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate_factory.h b/chromium/weblayer/browser/browsing_data_remover_delegate_factory.h
new file mode 100644
index 00000000000..b8c616fbfbc
--- /dev/null
+++ b/chromium/weblayer/browser/browsing_data_remover_delegate_factory.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_
+#define WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace weblayer {
+class BrowsingDataRemoverDelegate;
+
+class BrowsingDataRemoverDelegateFactory
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ BrowsingDataRemoverDelegateFactory(
+ const BrowsingDataRemoverDelegateFactory&) = delete;
+ BrowsingDataRemoverDelegateFactory& operator=(
+ const BrowsingDataRemoverDelegateFactory&) = delete;
+
+ static BrowsingDataRemoverDelegate* GetForBrowserContext(
+ content::BrowserContext* browser_context);
+ static BrowsingDataRemoverDelegateFactory* GetInstance();
+
+ private:
+ friend class base::NoDestructor<BrowsingDataRemoverDelegateFactory>;
+
+ BrowsingDataRemoverDelegateFactory();
+ ~BrowsingDataRemoverDelegateFactory() override;
+
+ // BrowserContextKeyedServiceFactory methods:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const override;
+ content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const override;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_FACTORY_H_
diff --git a/chromium/weblayer/browser/confirm_infobar_android.cc b/chromium/weblayer/browser/confirm_infobar_android.cc
new file mode 100644
index 00000000000..2ce1d44fa97
--- /dev/null
+++ b/chromium/weblayer/browser/confirm_infobar_android.cc
@@ -0,0 +1,95 @@
+// 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.
+
+#include "weblayer/browser/confirm_infobar_android.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/android/jni_string.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/android/window_android.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/image/image.h"
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/java/jni/ConfirmInfoBar_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace weblayer {
+
+// InfoBarService -------------------------------------------------------------
+
+std::unique_ptr<infobars::InfoBar> InfoBarService::CreateConfirmInfoBar(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate) {
+ return std::make_unique<ConfirmInfoBar>(std::move(delegate));
+}
+
+// ConfirmInfoBar -------------------------------------------------------------
+
+ConfirmInfoBar::ConfirmInfoBar(std::unique_ptr<ConfirmInfoBarDelegate> delegate)
+ : InfoBarAndroid(std::move(delegate)) {}
+
+ConfirmInfoBar::~ConfirmInfoBar() = default;
+
+base::string16 ConfirmInfoBar::GetTextFor(
+ ConfirmInfoBarDelegate::InfoBarButton button) {
+ ConfirmInfoBarDelegate* delegate = GetDelegate();
+ return (delegate->GetButtons() & button) ? delegate->GetButtonLabel(button)
+ : base::string16();
+}
+
+ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() {
+ return delegate()->AsConfirmInfoBarDelegate();
+}
+
+ScopedJavaLocalRef<jobject> ConfirmInfoBar::CreateRenderInfoBar(JNIEnv* env) {
+ ScopedJavaLocalRef<jstring> ok_button_text =
+ base::android::ConvertUTF16ToJavaString(
+ env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_OK));
+ ScopedJavaLocalRef<jstring> cancel_button_text =
+ base::android::ConvertUTF16ToJavaString(
+ env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_CANCEL));
+ ConfirmInfoBarDelegate* delegate = GetDelegate();
+ ScopedJavaLocalRef<jstring> message_text =
+ base::android::ConvertUTF16ToJavaString(env, delegate->GetMessageText());
+ ScopedJavaLocalRef<jstring> link_text =
+ base::android::ConvertUTF16ToJavaString(env, delegate->GetLinkText());
+
+ ScopedJavaLocalRef<jobject> java_bitmap;
+ if (delegate->GetIconId() == infobars::InfoBarDelegate::kNoIconID &&
+ !delegate->GetIcon().IsEmpty()) {
+ java_bitmap = gfx::ConvertToJavaBitmap(delegate->GetIcon().ToSkBitmap());
+ }
+
+ return Java_ConfirmInfoBar_create(env, GetJavaIconId(), java_bitmap,
+ message_text, link_text, ok_button_text,
+ cancel_button_text);
+}
+
+void ConfirmInfoBar::OnLinkClicked(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ if (!owner())
+ return; // We're closing; don't call anything, it might access the owner.
+
+ if (GetDelegate()->LinkClicked(WindowOpenDisposition::NEW_FOREGROUND_TAB))
+ RemoveSelf();
+}
+
+void ConfirmInfoBar::ProcessButton(int action) {
+ if (!owner())
+ return; // We're closing; don't call anything, it might access the owner.
+
+ DCHECK((action == InfoBarAndroid::ACTION_OK) ||
+ (action == InfoBarAndroid::ACTION_CANCEL));
+ ConfirmInfoBarDelegate* delegate = GetDelegate();
+ if ((action == InfoBarAndroid::ACTION_OK) ? delegate->Accept()
+ : delegate->Cancel()) {
+ RemoveSelf();
+ }
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/confirm_infobar_android.h b/chromium/weblayer/browser/confirm_infobar_android.h
new file mode 100644
index 00000000000..9611841ef3f
--- /dev/null
+++ b/chromium/weblayer/browser/confirm_infobar_android.h
@@ -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.
+
+#ifndef WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_
+#define WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "weblayer/browser/infobar_android.h"
+
+namespace weblayer {
+
+class ConfirmInfoBar : public InfoBarAndroid {
+ public:
+ explicit ConfirmInfoBar(std::unique_ptr<ConfirmInfoBarDelegate> delegate);
+ ~ConfirmInfoBar() override;
+
+ protected:
+ ConfirmInfoBarDelegate* GetDelegate();
+ base::string16 GetTextFor(ConfirmInfoBarDelegate::InfoBarButton button);
+
+ // InfoBarAndroid overrides.
+ base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+ JNIEnv* env) override;
+
+ void OnLinkClicked(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj) override;
+
+ void ProcessButton(int action) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConfirmInfoBar);
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_
diff --git a/chromium/weblayer/browser/content_browser_client_impl.cc b/chromium/weblayer/browser/content_browser_client_impl.cc
index 3f6d1ea6266..438122ad99b 100644
--- a/chromium/weblayer/browser/content_browser_client_impl.cc
+++ b/chromium/weblayer/browser/content_browser_client_impl.cc
@@ -17,6 +17,7 @@
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
+#include "components/blocked_content/popup_blocker.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/embedder_support/switches.h"
#include "components/network_time/network_time_tracker.h"
@@ -26,20 +27,26 @@
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service_factory.h"
+#include "components/prefs/scoped_user_pref_update.h"
#include "components/security_interstitials/content/ssl_cert_reporter.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "components/security_interstitials/content/ssl_error_navigation_throttle.h"
+#include "components/site_isolation/pref_names.h"
+#include "components/site_isolation/preloaded_isolated_origins.h"
+#include "components/site_isolation/site_isolation_policy.h"
#include "components/strings/grit/components_locale_settings.h"
-#include "components/variations/net/variations_http_headers.h"
+#include "components/user_prefs/user_prefs.h"
#include "components/variations/service/variations_service.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/cors_exempt_headers.h"
+#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/devtools_manager_delegate.h"
#include "content/public/browser/generated_code_cache_settings.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/tts_controller.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_names.mojom.h"
@@ -48,6 +55,7 @@
#include "content/public/common/window_container_type.mojom.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "net/proxy_resolution/proxy_config.h"
+#include "net/ssl/client_cert_identity.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/network_service.h"
#include "services/network/public/mojom/network_context.mojom.h"
@@ -55,6 +63,7 @@
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
@@ -62,8 +71,12 @@
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/download_manager_delegate_impl.h"
#include "weblayer/browser/feature_list_creator.h"
+#include "weblayer/browser/host_content_settings_map_factory.h"
+#include "weblayer/browser/http_auth_handler_impl.h"
#include "weblayer/browser/i18n_util.h"
#include "weblayer/browser/navigation_controller_impl.h"
+#include "weblayer/browser/password_manager_driver_factory.h"
+#include "weblayer/browser/popup_navigation_delegate_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/system_network_context_manager.h"
#include "weblayer/browser/tab_impl.h"
@@ -86,19 +99,24 @@
#include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/bind.h"
-#include "base/task/post_task.h"
+#include "components/browser_ui/client_certificate/android/ssl_client_certificate_request.h"
#include "components/cdm/browser/cdm_message_filter_android.h"
#include "components/cdm/browser/media_drm_storage_impl.h" // nogncheck
#include "components/crash/content/browser/crash_handler_host_linux.h"
#include "components/embedder_support/android/metrics/android_metrics_service_client.h"
#include "components/navigation_interception/intercept_navigation_delegate.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h" // nogncheck
+#include "components/safe_browsing/core/realtime/url_lookup_service.h" // nogncheck
#include "components/spellcheck/browser/spell_check_host_impl.h" // nogncheck
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "ui/base/resource/resource_bundle_android.h"
+#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
#include "weblayer/browser/android_descriptors.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/devtools_manager_delegate_android.h"
+#include "weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h"
#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
#endif
@@ -333,8 +351,6 @@ void ContentBrowserClientImpl::ConfigureNetworkContextParams(
proxy_config,
net::DefineNetworkTrafficAnnotation("undefined", "Nothing here yet."));
}
- content::UpdateCorsExemptHeader(context_params);
- variations::UpdateCorsExemptHeaderForVariations(context_params);
}
void ContentBrowserClientImpl::OnNetworkServiceCreated(
@@ -359,8 +375,27 @@ ContentBrowserClientImpl::CreateURLLoaderThrottles(
if (base::FeatureList::IsEnabled(features::kWebLayerSafeBrowsing) &&
IsSafebrowsingSupported()) {
#if defined(OS_ANDROID)
- result.push_back(GetSafeBrowsingService()->CreateURLLoaderThrottle(
- wc_getter, frame_tree_node_id));
+ BrowserContextImpl* browser_context_impl =
+ static_cast<BrowserContextImpl*>(browser_context);
+ bool is_safe_browsing_enabled = safe_browsing::IsSafeBrowsingEnabled(
+ *browser_context_impl->pref_service());
+
+ if (is_safe_browsing_enabled) {
+ bool is_real_time_lookup_enabled =
+ safe_browsing::RealTimePolicyEngine::CanPerformFullURLLookup(
+ browser_context_impl->pref_service(),
+ browser_context_impl->IsOffTheRecord(),
+ FeatureListCreator::GetInstance()->variations_service());
+
+ // |url_lookup_service| is used when real time url check is enabled.
+ safe_browsing::RealTimeUrlLookupServiceBase* url_lookup_service =
+ is_real_time_lookup_enabled
+ ? RealTimeUrlLookupServiceFactory::GetForBrowserContext(
+ browser_context)
+ : nullptr;
+ result.push_back(GetSafeBrowsingService()->CreateURLLoaderThrottle(
+ wc_getter, frame_tree_node_id, url_lookup_service));
+ }
#endif
}
@@ -410,6 +445,49 @@ bool ContentBrowserClientImpl::IsHandledURL(const GURL& url) {
return false;
}
+std::vector<url::Origin>
+ContentBrowserClientImpl::GetOriginsRequiringDedicatedProcess() {
+ return site_isolation::GetBrowserSpecificBuiltInIsolatedOrigins();
+}
+
+bool ContentBrowserClientImpl::ShouldDisableSiteIsolation() {
+ return site_isolation::SiteIsolationPolicy::
+ ShouldDisableSiteIsolationDueToMemoryThreshold();
+}
+
+std::vector<std::string>
+ContentBrowserClientImpl::GetAdditionalSiteIsolationModes() {
+ if (site_isolation::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled())
+ return {"Isolate Password Sites"};
+ return {};
+}
+
+void ContentBrowserClientImpl::PersistIsolatedOrigin(
+ content::BrowserContext* context,
+ const url::Origin& origin) {
+ DCHECK(!context->IsOffTheRecord());
+ ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
+ site_isolation::prefs::kUserTriggeredIsolatedOrigins);
+ base::ListValue* list = update.Get();
+ base::Value value(origin.Serialize());
+ if (!base::Contains(list->GetList(), value))
+ list->Append(std::move(value));
+}
+
+base::OnceClosure ContentBrowserClientImpl::SelectClientCertificate(
+ content::WebContents* web_contents,
+ net::SSLCertRequestInfo* cert_request_info,
+ net::ClientCertIdentityList client_certs,
+ std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+#if defined(OS_ANDROID)
+ return browser_ui::ShowSSLClientCertificateSelector(
+ web_contents, cert_request_info, std::move(delegate));
+#else
+ delegate->ContinueWithCertificate(nullptr, nullptr);
+ return base::OnceClosure();
+#endif
+}
+
bool ContentBrowserClientImpl::CanCreateWindow(
content::RenderFrameHost* opener,
const GURL& opener_url,
@@ -441,11 +519,6 @@ bool ContentBrowserClientImpl::CanCreateWindow(
return false;
}
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- embedder_support::kDisablePopupBlocking)) {
- return true;
- }
-
// WindowOpenDisposition has a *ton* of types, but the following are really
// the only ones that should be hit for this code path.
switch (disposition) {
@@ -461,8 +534,27 @@ bool ContentBrowserClientImpl::CanCreateWindow(
return false;
}
- // TODO(https://crbug.com/1019922): support proper popup blocking.
- return user_gesture;
+ GURL popup_url(target_url);
+ web_contents->GetMainFrame()->GetProcess()->FilterURL(false, &popup_url);
+ // Use ui::PAGE_TRANSITION_LINK to match the similar logic in //chrome.
+ content::OpenURLParams params(popup_url, referrer, disposition,
+ ui::PAGE_TRANSITION_LINK,
+ /*is_renderer_initiated*/ true);
+ params.user_gesture = user_gesture;
+ params.initiator_origin = source_origin;
+ params.source_render_frame_id = opener->GetRoutingID();
+ params.source_render_process_id = opener->GetProcess()->GetID();
+ params.source_site_instance = opener->GetSiteInstance();
+ // The content::OpenURLParams are created just for the delegate, and do not
+ // correspond to actual params created by //content, so pass null for the
+ // |open_url_params| argument here.
+ return blocked_content::MaybeBlockPopup(
+ web_contents, &opener_top_level_frame_url,
+ std::make_unique<PopupNavigationDelegateImpl>(params, web_contents,
+ opener),
+ /*open_url_params*/ nullptr, features,
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ web_contents->GetBrowserContext())) != nullptr;
}
std::vector<std::unique_ptr<content::NavigationThrottle>>
@@ -536,6 +628,13 @@ bool ContentBrowserClientImpl::BindAssociatedReceiverFromFrame(
render_frame_host);
return true;
}
+ if (interface_name == autofill::mojom::PasswordManagerDriver::Name_) {
+ PasswordManagerDriverFactory::BindPasswordManagerDriver(
+ mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>(
+ std::move(*handle)),
+ render_frame_host);
+ return true;
+ }
return false;
}
@@ -550,9 +649,8 @@ void ContentBrowserClientImpl::ExposeInterfacesToRenderer(
mojo::MakeSelfOwnedReceiver(std::make_unique<SpellCheckHostImpl>(),
std::move(receiver));
};
- registry->AddInterface(
- base::BindRepeating(create_spellcheck_host),
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}));
+ registry->AddInterface(base::BindRepeating(create_spellcheck_host),
+ content::GetUIThreadTaskRunner({}));
if (base::FeatureList::IsEnabled(features::kWebLayerSafeBrowsing) &&
IsSafebrowsingSupported()) {
@@ -593,6 +691,12 @@ ContentBrowserClientImpl::CreateQuotaPermissionContext() {
return base::MakeRefCounted<permissions::QuotaPermissionContextImpl>();
}
+content::TtsPlatform* ContentBrowserClientImpl::GetTtsPlatform() {
+ // TODO(sky): figure out a better way to integrate this.
+ content::TtsController::GetInstance()->SetStopSpeakingWhenHidden(true);
+ return nullptr;
+}
+
void ContentBrowserClientImpl::CreateFeatureListAndFieldTrials() {
local_state_ = CreateLocalState();
feature_list_creator_ =
@@ -690,6 +794,21 @@ ContentBrowserClientImpl::GetWideColorGamutHeuristic() {
// flinger.
return WideColorGamutHeuristic::kUseWindow;
}
+
+std::unique_ptr<content::LoginDelegate>
+ContentBrowserClientImpl::CreateLoginDelegate(
+ const net::AuthChallengeInfo& auth_info,
+ content::WebContents* web_contents,
+ const content::GlobalRequestID& request_id,
+ bool is_main_frame,
+ const GURL& url,
+ scoped_refptr<net::HttpResponseHeaders> response_headers,
+ bool first_auth_attempt,
+ LoginAuthRequiredCallback auth_required_callback) {
+ return std::make_unique<HttpAuthHandlerImpl>(
+ auth_info, web_contents, first_auth_attempt,
+ std::move(auth_required_callback));
+}
#endif // OS_ANDROID
content::SpeechRecognitionManagerDelegate*
@@ -697,4 +816,12 @@ ContentBrowserClientImpl::CreateSpeechRecognitionManagerDelegate() {
return new WebLayerSpeechRecognitionManagerDelegate();
}
+ukm::UkmService* ContentBrowserClientImpl::GetUkmService() {
+#if defined(OS_ANDROID)
+ return WebLayerMetricsServiceClient::GetInstance()->GetUkmService();
+#else
+ return nullptr;
+#endif
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/content_browser_client_impl.h b/chromium/weblayer/browser/content_browser_client_impl.h
index 42d9b3c13e9..a8c2ea7c804 100644
--- a/chromium/weblayer/browser/content_browser_client_impl.h
+++ b/chromium/weblayer/browser/content_browser_client_impl.h
@@ -65,6 +65,16 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient {
content::NavigationUIData* navigation_ui_data,
int frame_tree_node_id) override;
bool IsHandledURL(const GURL& url) override;
+ std::vector<url::Origin> GetOriginsRequiringDedicatedProcess() override;
+ bool ShouldDisableSiteIsolation() override;
+ std::vector<std::string> GetAdditionalSiteIsolationModes() override;
+ void PersistIsolatedOrigin(content::BrowserContext* context,
+ const url::Origin& origin) override;
+ base::OnceClosure SelectClientCertificate(
+ content::WebContents* web_contents,
+ net::SSLCertRequestInfo* cert_request_info,
+ net::ClientCertIdentityList client_certs,
+ std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
bool CanCreateWindow(content::RenderFrameHost* opener,
const GURL& opener_url,
const GURL& opener_top_level_frame_url,
@@ -111,11 +121,22 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient {
int child_process_id) override;
#if defined(OS_ANDROID)
WideColorGamutHeuristic GetWideColorGamutHeuristic() override;
+ std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
+ const net::AuthChallengeInfo& auth_info,
+ content::WebContents* web_contents,
+ const content::GlobalRequestID& request_id,
+ bool is_main_frame,
+ const GURL& url,
+ scoped_refptr<net::HttpResponseHeaders> response_headers,
+ bool first_auth_attempt,
+ LoginAuthRequiredCallback auth_required_callback) override;
#endif // OS_ANDROID
-
- void CreateFeatureListAndFieldTrials();
content::SpeechRecognitionManagerDelegate*
CreateSpeechRecognitionManagerDelegate() override;
+ ukm::UkmService* GetUkmService() override;
+ content::TtsPlatform* GetTtsPlatform() override;
+
+ void CreateFeatureListAndFieldTrials();
private:
std::unique_ptr<PrefService> CreateLocalState();
diff --git a/chromium/weblayer/browser/content_view_render_view.cc b/chromium/weblayer/browser/content_view_render_view.cc
index 126f8db191d..98cd8bcf37a 100644
--- a/chromium/weblayer/browser/content_view_render_view.cc
+++ b/chromium/weblayer/browser/content_view_render_view.cc
@@ -8,6 +8,7 @@
#include <android/native_window_jni.h>
#include <memory>
+#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
@@ -37,7 +38,15 @@ ContentViewRenderView::ContentViewRenderView(JNIEnv* env,
java_obj_.Reset(env, obj);
}
-ContentViewRenderView::~ContentViewRenderView() = default;
+ContentViewRenderView::~ContentViewRenderView() {
+ DCHECK(height_changed_listener_.is_null());
+}
+
+void ContentViewRenderView::SetHeightChangedListener(
+ base::RepeatingClosure callback) {
+ DCHECK(height_changed_listener_.is_null() || callback.is_null());
+ height_changed_listener_ = std::move(callback);
+}
// static
static jlong JNI_ContentViewRenderView_Init(
@@ -75,11 +84,15 @@ void ContentViewRenderView::OnPhysicalBackingSizeChanged(
const JavaParamRef<jobject>& jweb_contents,
jint width,
jint height) {
+ bool height_changed = height_ != height;
height_ = height;
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(jweb_contents);
gfx::Size size(width, height);
web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size);
+
+ if (height_changed && !height_changed_listener_.is_null())
+ height_changed_listener_.Run();
}
void ContentViewRenderView::SurfaceCreated(JNIEnv* env) {
@@ -109,6 +122,10 @@ void ContentViewRenderView::SurfaceChanged(
compositor_->SetWindowBounds(gfx::Size(width, height));
}
+void ContentViewRenderView::SetNeedsRedraw(JNIEnv* env) {
+ compositor_->SetNeedsRedraw();
+}
+
base::android::ScopedJavaLocalRef<jobject>
ContentViewRenderView::GetResourceManager(JNIEnv* env) {
return compositor_->GetResourceManager().GetJavaObject();
@@ -127,6 +144,13 @@ void ContentViewRenderView::DidSwapFrame(int pending_frames) {
}
}
+void ContentViewRenderView::DidSwapBuffers(const gfx::Size& swap_size) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ bool matches_window_bounds = swap_size == compositor_->GetWindowBounds();
+ Java_ContentViewRenderView_didSwapBuffers(env, java_obj_,
+ matches_window_bounds);
+}
+
void ContentViewRenderView::EvictCachedSurface(JNIEnv* env) {
compositor_->EvictCachedBackBuffer();
}
diff --git a/chromium/weblayer/browser/content_view_render_view.h b/chromium/weblayer/browser/content_view_render_view.h
index c325db253e7..3a28dac389f 100644
--- a/chromium/weblayer/browser/content_view_render_view.h
+++ b/chromium/weblayer/browser/content_view_render_view.h
@@ -8,7 +8,7 @@
#include <memory>
#include "base/android/jni_weak_ref.h"
-#include "base/logging.h"
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/android/compositor_client.h"
@@ -38,6 +38,7 @@ class ContentViewRenderView : public content::CompositorClient {
// Height, in pixels.
int height() const { return height_; }
+ void SetHeightChangedListener(base::RepeatingClosure callback);
// Methods called from Java via JNI -----------------------------------------
void Destroy(JNIEnv* env);
@@ -57,12 +58,14 @@ class ContentViewRenderView : public content::CompositorClient {
jint width,
jint height,
const base::android::JavaParamRef<jobject>& surface);
+ void SetNeedsRedraw(JNIEnv* env);
void EvictCachedSurface(JNIEnv* env);
base::android::ScopedJavaLocalRef<jobject> GetResourceManager(JNIEnv* env);
// CompositorClient implementation
void UpdateLayerTreeHost() override;
void DidSwapFrame(int pending_frames) override;
+ void DidSwapBuffers(const gfx::Size& swap_size) override;
private:
~ContentViewRenderView() override;
@@ -81,6 +84,7 @@ class ContentViewRenderView : public content::CompositorClient {
int current_surface_format_ = 0;
+ base::RepeatingClosure height_changed_listener_;
int height_ = 0;
DISALLOW_COPY_AND_ASSIGN(ContentViewRenderView);
diff --git a/chromium/weblayer/browser/cookie_manager_impl.cc b/chromium/weblayer/browser/cookie_manager_impl.cc
index 8cb70b3195a..ae1392cb88e 100644
--- a/chromium/weblayer/browser/cookie_manager_impl.cc
+++ b/chromium/weblayer/browser/cookie_manager_impl.cc
@@ -19,9 +19,9 @@ namespace weblayer {
namespace {
void GetCookieComplete(CookieManager::GetCookieCallback callback,
- const net::CookieStatusList& cookies,
- const net::CookieStatusList& excluded_cookies) {
- net::CookieList cookie_list = net::cookie_util::StripStatuses(cookies);
+ const net::CookieAccessResultList& cookies,
+ const net::CookieAccessResultList& excluded_cookies) {
+ net::CookieList cookie_list = net::cookie_util::StripAccessResults(cookies);
std::move(callback).Run(net::CanonicalCookie::BuildCookieLine(cookie_list));
}
diff --git a/chromium/weblayer/browser/download_manager_delegate_impl.cc b/chromium/weblayer/browser/download_manager_delegate_impl.cc
index 7e83c6be7ca..00c35b6b4a7 100644
--- a/chromium/weblayer/browser/download_manager_delegate_impl.cc
+++ b/chromium/weblayer/browser/download_manager_delegate_impl.cc
@@ -5,13 +5,14 @@
#include "weblayer/browser/download_manager_delegate_impl.h"
#include "base/files/file_util.h"
-#include "base/task/post_task.h"
+#include "base/optional.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
#include "components/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager.h"
#include "net/base/filename_util.h"
@@ -42,8 +43,8 @@ void GenerateFilename(
base::CreateDirectory(suggested_directory);
base::FilePath suggested_path(suggested_directory.Append(generated_name));
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(std::move(callback), suggested_path));
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), suggested_path));
}
} // namespace
@@ -89,7 +90,8 @@ bool DownloadManagerDelegateImpl::DetermineDownloadTarget(
download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download::DownloadItem::MixedContentStatus::UNKNOWN,
- item->GetForcedFilePath(), download::DOWNLOAD_INTERRUPT_REASON_NONE);
+ item->GetForcedFilePath(), base::nullopt /*download_schedule*/,
+ download::DOWNLOAD_INTERRUPT_REASON_NONE);
return true;
}
@@ -240,6 +242,7 @@ void DownloadManagerDelegateImpl::OnDownloadPathGenerated(
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download::DownloadItem::MixedContentStatus::UNKNOWN,
suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
+ base::nullopt /*download_schedule*/,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
diff --git a/chromium/weblayer/browser/feature_list_creator.cc b/chromium/weblayer/browser/feature_list_creator.cc
index 3a4e7460d28..4ff04637b63 100644
--- a/chromium/weblayer/browser/feature_list_creator.cc
+++ b/chromium/weblayer/browser/feature_list_creator.cc
@@ -85,13 +85,12 @@ void FeatureListCreator::SetUpFieldTrials() {
variations_service_->OverridePlatform(
variations::Study::PLATFORM_ANDROID_WEBLAYER, "android_weblayer");
- std::set<std::string> unforceable_field_trials;
std::vector<std::string> variation_ids;
auto feature_list = std::make_unique<base::FeatureList>();
variations_service_->SetupFieldTrials(
cc::switches::kEnableGpuBenchmarking, switches::kEnableFeatures,
- switches::kDisableFeatures, unforceable_field_trials, variation_ids,
+ switches::kDisableFeatures, variation_ids,
content::GetSwitchDependentFeatureOverrides(
*base::CommandLine::ForCurrentProcess()),
std::move(feature_list), &weblayer_field_trials_);
diff --git a/chromium/weblayer/browser/http_auth_handler_impl.cc b/chromium/weblayer/browser/http_auth_handler_impl.cc
new file mode 100644
index 00000000000..bb9823e2a07
--- /dev/null
+++ b/chromium/weblayer/browser/http_auth_handler_impl.cc
@@ -0,0 +1,50 @@
+// 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.
+
+#include "weblayer/browser/http_auth_handler_impl.h"
+
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/auth.h"
+#include "weblayer/browser/tab_impl.h"
+
+namespace weblayer {
+
+HttpAuthHandlerImpl::HttpAuthHandlerImpl(
+ const net::AuthChallengeInfo& auth_info,
+ content::WebContents* web_contents,
+ bool first_auth_attempt,
+ LoginAuthRequiredCallback callback)
+ : WebContentsObserver(web_contents), callback_(std::move(callback)) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ url_ = auth_info.challenger.GetURL().Resolve(auth_info.path);
+
+ auto* tab = TabImpl::FromWebContents(web_contents);
+ tab->ShowHttpAuthPrompt(this);
+}
+
+HttpAuthHandlerImpl::~HttpAuthHandlerImpl() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ auto* tab = TabImpl::FromWebContents(web_contents());
+ if (tab)
+ tab->CloseHttpAuthPrompt();
+}
+
+void HttpAuthHandlerImpl::Proceed(const base::string16& user,
+ const base::string16& password) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (callback_) {
+ std::move(callback_).Run(net::AuthCredentials(user, password));
+ }
+}
+
+void HttpAuthHandlerImpl::Cancel() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (callback_) {
+ std::move(callback_).Run(base::nullopt);
+ }
+}
+
+} // namespace weblayer \ No newline at end of file
diff --git a/chromium/weblayer/browser/http_auth_handler_impl.h b/chromium/weblayer/browser/http_auth_handler_impl.h
new file mode 100644
index 00000000000..a68e0c1d581
--- /dev/null
+++ b/chromium/weblayer/browser/http_auth_handler_impl.h
@@ -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.
+
+#ifndef WEBLAYER_BROWSER_HTTP_AUTH_HANDLER_IMPL_H_
+#define WEBLAYER_BROWSER_HTTP_AUTH_HANDLER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/login_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "url/gurl.h"
+
+namespace weblayer {
+
+// Implements support for http auth.
+class HttpAuthHandlerImpl : public content::LoginDelegate,
+ public content::WebContentsObserver {
+ public:
+ HttpAuthHandlerImpl(const net::AuthChallengeInfo& auth_info,
+ content::WebContents* web_contents,
+ bool first_auth_attempt,
+ LoginAuthRequiredCallback callback);
+ ~HttpAuthHandlerImpl() override;
+
+ void Proceed(const base::string16& user, const base::string16& password);
+ void Cancel();
+
+ GURL url() { return url_; }
+
+ private:
+ GURL url_;
+ LoginAuthRequiredCallback callback_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_HTTP_AUTH_HANDLER_IMPL_H_ \ No newline at end of file
diff --git a/chromium/weblayer/browser/infobar_android.cc b/chromium/weblayer/browser/infobar_android.cc
new file mode 100644
index 00000000000..4aa24af9285
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_android.cc
@@ -0,0 +1,91 @@
+// 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.
+
+#include "weblayer/browser/infobar_android.h"
+
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/strings/string_util.h"
+#include "components/infobars/core/infobar.h"
+#include "components/infobars/core/infobar_delegate.h"
+#include "weblayer/browser/android/resource_mapper.h"
+#include "weblayer/browser/java/jni/InfoBar_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+
+namespace weblayer {
+
+// InfoBarAndroid -------------------------------------------------------------
+
+InfoBarAndroid::InfoBarAndroid(
+ std::unique_ptr<infobars::InfoBarDelegate> delegate)
+ : infobars::InfoBar(std::move(delegate)) {}
+
+InfoBarAndroid::~InfoBarAndroid() {
+ if (!java_info_bar_.is_null()) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_InfoBar_onNativeDestroyed(env, java_info_bar_);
+ }
+}
+
+void InfoBarAndroid::ReassignJavaInfoBar(InfoBarAndroid* replacement) {
+ DCHECK(replacement);
+ if (!java_info_bar_.is_null()) {
+ replacement->SetJavaInfoBar(java_info_bar_);
+ java_info_bar_.Reset();
+ }
+}
+
+void InfoBarAndroid::SetJavaInfoBar(
+ const base::android::JavaRef<jobject>& java_info_bar) {
+ DCHECK(java_info_bar_.is_null());
+ java_info_bar_.Reset(java_info_bar);
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_InfoBar_setNativeInfoBar(env, java_info_bar,
+ reinterpret_cast<intptr_t>(this));
+}
+
+const JavaRef<jobject>& InfoBarAndroid::GetJavaInfoBar() {
+ return java_info_bar_;
+}
+
+bool InfoBarAndroid::HasSetJavaInfoBar() const {
+ return !java_info_bar_.is_null();
+}
+
+int InfoBarAndroid::GetInfoBarIdentifier(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ return delegate()->GetIdentifier();
+}
+
+void InfoBarAndroid::OnButtonClicked(JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ jint action) {
+ ProcessButton(action);
+}
+
+void InfoBarAndroid::OnCloseButtonClicked(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ if (!owner())
+ return; // We're closing; don't call anything, it might access the owner.
+ delegate()->InfoBarDismissed();
+ RemoveSelf();
+}
+
+void InfoBarAndroid::CloseJavaInfoBar() {
+ if (!java_info_bar_.is_null()) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_InfoBar_closeInfoBar(env, java_info_bar_);
+ java_info_bar_.Reset(nullptr);
+ }
+}
+
+int InfoBarAndroid::GetJavaIconId() {
+ return weblayer::MapToJavaDrawableId(delegate()->GetIconId());
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/infobar_android.h b/chromium/weblayer/browser/infobar_android.h
new file mode 100644
index 00000000000..5c319688cff
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_android.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_INFOBAR_ANDROID_H_
+#define WEBLAYER_BROWSER_INFOBAR_ANDROID_H_
+
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "components/infobars/core/infobar.h"
+
+namespace infobars {
+class InfoBarDelegate;
+}
+
+namespace weblayer {
+
+class InfoBarAndroid : public infobars::InfoBar {
+ public:
+ // A Java counterpart will be generated for this enum.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private
+ // GENERATED_JAVA_PREFIX_TO_STRIP: ACTION_
+ enum ActionType {
+ ACTION_NONE = 0,
+ // Confirm infobar
+ ACTION_OK = 1,
+ ACTION_CANCEL = 2,
+ // Translate infobar
+ ACTION_TRANSLATE = 3,
+ ACTION_TRANSLATE_SHOW_ORIGINAL = 4,
+ };
+
+ explicit InfoBarAndroid(std::unique_ptr<infobars::InfoBarDelegate> delegate);
+ ~InfoBarAndroid() override;
+
+ // InfoBar:
+ virtual base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+ JNIEnv* env) = 0;
+
+ virtual void SetJavaInfoBar(
+ const base::android::JavaRef<jobject>& java_info_bar);
+ const base::android::JavaRef<jobject>& GetJavaInfoBar();
+ bool HasSetJavaInfoBar() const;
+
+ // Tells the Java-side counterpart of this InfoBar to point to the replacement
+ // InfoBar instead of this one.
+ void ReassignJavaInfoBar(InfoBarAndroid* replacement);
+
+ int GetInfoBarIdentifier(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+ virtual void OnLinkClicked(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj) {}
+ void OnButtonClicked(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jint action);
+ void OnCloseButtonClicked(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+
+ void CloseJavaInfoBar();
+
+ // Maps from a Chromium ID (IDR_TRANSLATE) to a Drawable ID.
+ int GetJavaIconId();
+
+ // Acquire the java infobar from a different one. This is used to do in-place
+ // replacements.
+ virtual void PassJavaInfoBar(InfoBarAndroid* source) {}
+
+ protected:
+ // Derived classes must implement this method to process the corresponding
+ // action.
+ virtual void ProcessButton(int action) = 0;
+
+ void CloseInfoBar();
+ InfoBarAndroid* infobar_android() { return this; }
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> java_info_bar_;
+
+ DISALLOW_COPY_AND_ASSIGN(InfoBarAndroid);
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_INFOBAR_ANDROID_H_
diff --git a/chromium/weblayer/browser/infobar_container_android.cc b/chromium/weblayer/browser/infobar_container_android.cc
new file mode 100644
index 00000000000..368ab764ce8
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_container_android.cc
@@ -0,0 +1,114 @@
+// 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.
+
+#include "weblayer/browser/infobar_container_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/check.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "components/infobars/core/infobar.h"
+#include "components/infobars/core/infobar_delegate.h"
+#include "content/public/browser/web_contents.h"
+#include "weblayer/browser/infobar_android.h"
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/java/jni/InfoBarContainer_jni.h"
+
+using base::android::JavaParamRef;
+
+namespace weblayer {
+
+// InfoBarContainerAndroid ----------------------------------------------------
+
+InfoBarContainerAndroid::InfoBarContainerAndroid(JNIEnv* env, jobject obj)
+ : infobars::InfoBarContainer(NULL),
+ weak_java_infobar_container_(env, obj) {}
+
+InfoBarContainerAndroid::~InfoBarContainerAndroid() {
+ RemoveAllInfoBarsForDestruction();
+}
+
+void InfoBarContainerAndroid::SetWebContents(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& web_contents) {
+ weblayer::InfoBarService* infobar_service =
+ web_contents
+ ? weblayer::InfoBarService::FromWebContents(
+ content::WebContents::FromJavaWebContents(web_contents))
+ : nullptr;
+ ChangeInfoBarManager(infobar_service);
+}
+
+void InfoBarContainerAndroid::Destroy(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ delete this;
+}
+
+void InfoBarContainerAndroid::PlatformSpecificAddInfoBar(
+ infobars::InfoBar* infobar,
+ size_t position) {
+ DCHECK(infobar);
+ InfoBarAndroid* android_bar = static_cast<InfoBarAndroid*>(infobar);
+ if (!android_bar) {
+ // TODO(bulach): CLANK: implement other types of InfoBars.
+ NOTIMPLEMENTED() << "CLANK: infobar identifier "
+ << infobar->delegate()->GetIdentifier();
+ return;
+ }
+
+ AttachJavaInfoBar(android_bar);
+}
+
+void InfoBarContainerAndroid::AttachJavaInfoBar(InfoBarAndroid* android_bar) {
+ if (android_bar->HasSetJavaInfoBar())
+ return;
+ JNIEnv* env = base::android::AttachCurrentThread();
+
+ if (Java_InfoBarContainer_hasInfoBars(
+ env, weak_java_infobar_container_.get(env))) {
+ base::UmaHistogramSparse("InfoBar.Shown.Hidden",
+ android_bar->delegate()->GetIdentifier());
+ infobars::InfoBarDelegate::InfoBarIdentifier identifier =
+ static_cast<infobars::InfoBarDelegate::InfoBarIdentifier>(
+ Java_InfoBarContainer_getTopInfoBarIdentifier(
+ env, weak_java_infobar_container_.get(env)));
+ if (identifier != infobars::InfoBarDelegate::InfoBarIdentifier::INVALID) {
+ base::UmaHistogramSparse("InfoBar.Shown.Hiding", identifier);
+ }
+ } else {
+ base::UmaHistogramSparse("InfoBar.Shown.Visible",
+ android_bar->delegate()->GetIdentifier());
+ }
+
+ base::android::ScopedJavaLocalRef<jobject> java_infobar =
+ android_bar->CreateRenderInfoBar(env);
+ android_bar->SetJavaInfoBar(java_infobar);
+ Java_InfoBarContainer_addInfoBar(env, weak_java_infobar_container_.get(env),
+ java_infobar);
+}
+
+void InfoBarContainerAndroid::PlatformSpecificReplaceInfoBar(
+ infobars::InfoBar* old_infobar,
+ infobars::InfoBar* new_infobar) {
+ static_cast<InfoBarAndroid*>(new_infobar)
+ ->PassJavaInfoBar(static_cast<InfoBarAndroid*>(old_infobar));
+}
+
+void InfoBarContainerAndroid::PlatformSpecificRemoveInfoBar(
+ infobars::InfoBar* infobar) {
+ InfoBarAndroid* android_infobar = static_cast<InfoBarAndroid*>(infobar);
+ android_infobar->CloseJavaInfoBar();
+}
+
+// Native JNI methods ---------------------------------------------------------
+
+static jlong JNI_InfoBarContainer_Init(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ InfoBarContainerAndroid* infobar_container =
+ new InfoBarContainerAndroid(env, obj);
+ return reinterpret_cast<intptr_t>(infobar_container);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/infobar_container_android.h b/chromium/weblayer/browser/infobar_container_android.h
new file mode 100644
index 00000000000..4069cf6b19e
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_container_android.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_INFOBAR_CONTAINER_ANDROID_H_
+#define WEBLAYER_BROWSER_INFOBAR_CONTAINER_ANDROID_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/infobars/core/infobar_container.h"
+
+namespace weblayer {
+
+class InfoBarAndroid;
+
+class InfoBarContainerAndroid : public infobars::InfoBarContainer {
+ public:
+ InfoBarContainerAndroid(JNIEnv* env, jobject infobar_container);
+ void SetWebContents(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ const base::android::JavaParamRef<jobject>& web_contents);
+ void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+
+ JavaObjectWeakGlobalRef java_container() const {
+ return weak_java_infobar_container_;
+ }
+
+ private:
+ ~InfoBarContainerAndroid() override;
+
+ // InfobarContainer:
+ void PlatformSpecificAddInfoBar(infobars::InfoBar* infobar,
+ size_t position) override;
+ void PlatformSpecificRemoveInfoBar(infobars::InfoBar* infobar) override;
+ void PlatformSpecificReplaceInfoBar(infobars::InfoBar* old_infobar,
+ infobars::InfoBar* new_infobar) override;
+
+ // Create the Java equivalent of |android_bar| and add it to the java
+ // container.
+ void AttachJavaInfoBar(InfoBarAndroid* android_bar);
+
+ // We're owned by the java infobar, need to use a weak ref so it can destroy
+ // us.
+ JavaObjectWeakGlobalRef weak_java_infobar_container_;
+
+ DISALLOW_COPY_AND_ASSIGN(InfoBarContainerAndroid);
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_INFOBAR_CONTAINER_ANDROID_H_
diff --git a/chromium/weblayer/browser/infobar_service.cc b/chromium/weblayer/browser/infobar_service.cc
new file mode 100644
index 00000000000..37af3091f35
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_service.cc
@@ -0,0 +1,25 @@
+// 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.
+
+#include "weblayer/browser/infobar_service.h"
+
+namespace weblayer {
+
+InfoBarService::InfoBarService(content::WebContents* web_contents)
+ : infobars::ContentInfoBarManager(web_contents) {}
+
+InfoBarService::~InfoBarService() {}
+
+void InfoBarService::WebContentsDestroyed() {
+ // The WebContents is going away; be aggressively paranoid and delete
+ // ourselves lest other parts of the system attempt to add infobars or use
+ // us otherwise during the destruction.
+ web_contents()->RemoveUserData(UserDataKey());
+ // That was the equivalent of "delete this". This object is now destroyed;
+ // returning from this function is the only safe thing to do.
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(InfoBarService)
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/infobar_service.h b/chromium/weblayer/browser/infobar_service.h
new file mode 100644
index 00000000000..95cb1ec184b
--- /dev/null
+++ b/chromium/weblayer/browser/infobar_service.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_INFOBAR_SERVICE_H_
+#define WEBLAYER_BROWSER_INFOBAR_SERVICE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "components/infobars/content/content_infobar_manager.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace weblayer {
+
+// WebLayer's specialization of ContentInfoBarManager, which ties the lifetime
+// of ContentInfoBarManager instances to that of the WebContents with which they
+// are associated.
+class InfoBarService : public infobars::ContentInfoBarManager,
+ public content::WebContentsUserData<InfoBarService> {
+ public:
+ ~InfoBarService() override;
+ InfoBarService(const InfoBarService&) = delete;
+ InfoBarService& operator=(const InfoBarService&) = delete;
+
+ // InfoBarManager:
+ std::unique_ptr<infobars::InfoBar> CreateConfirmInfoBar(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate) override;
+
+ protected:
+ explicit InfoBarService(content::WebContents* web_contents);
+
+ private:
+ friend class content::WebContentsUserData<InfoBarService>;
+
+ // infobars::ContentInfoBarManager:
+ void WebContentsDestroyed() override;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_INFOBAR_SERVICE_H_
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>
diff --git a/chromium/weblayer/browser/js_communication/web_message_browsertest.cc b/chromium/weblayer/browser/js_communication/web_message_browsertest.cc
new file mode 100644
index 00000000000..8d147c10be7
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_browsertest.cc
@@ -0,0 +1,112 @@
+// 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.
+
+#include "weblayer/public/js_communication/web_message.h"
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "weblayer/public/js_communication/web_message.h"
+#include "weblayer/public/js_communication/web_message_host.h"
+#include "weblayer/public/js_communication/web_message_host_factory.h"
+#include "weblayer/public/js_communication/web_message_reply_proxy.h"
+#include "weblayer/public/navigation.h"
+#include "weblayer/public/navigation_controller.h"
+#include "weblayer/public/tab.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/weblayer_browser_test.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+
+namespace {
+
+class WebMessageHostImpl;
+WebMessageHostImpl* current_connection = nullptr;
+
+// WebMessageHost implementation that records contents of OnPostMessage().
+class WebMessageHostImpl : public WebMessageHost {
+ public:
+ WebMessageHostImpl(base::RepeatingClosure quit_closure,
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* proxy)
+ : quit_closure_(quit_closure), proxy_(proxy) {
+ current_connection = this;
+ }
+ ~WebMessageHostImpl() override {
+ if (current_connection == this)
+ current_connection = nullptr;
+ }
+
+ std::vector<base::string16>& messages() { return messages_; }
+
+ // WebMessageHost:
+ void OnPostMessage(std::unique_ptr<WebMessage> message) override {
+ messages_.push_back(std::move(message->message));
+ if (++call_count_ == 1) {
+ // First time called, send a message to the page.
+ std::unique_ptr<WebMessage> m2 = std::make_unique<WebMessage>();
+ m2->message = base::ASCIIToUTF16("from c++");
+ proxy_->PostMessage(std::move(m2));
+ } else {
+ // On subsequent calls quit.
+ quit_closure_.Run();
+ }
+ }
+
+ private:
+ int call_count_ = 0;
+ base::RepeatingClosure quit_closure_;
+ WebMessageReplyProxy* proxy_;
+ std::vector<base::string16> messages_;
+};
+
+// WebMessageHostFactory implementation that creates WebMessageHostImp.
+class WebMessageHostFactoryImpl : public WebMessageHostFactory {
+ public:
+ explicit WebMessageHostFactoryImpl(base::RepeatingClosure quit_closure)
+ : quit_closure_(quit_closure) {}
+ ~WebMessageHostFactoryImpl() override = default;
+
+ // WebMessageHostFactory:
+ std::unique_ptr<WebMessageHost> CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* proxy) override {
+ return std::make_unique<WebMessageHostImpl>(quit_closure_, origin_string,
+ is_main_frame, proxy);
+ }
+
+ private:
+ base::RepeatingClosure quit_closure_;
+};
+
+} // namespace
+
+using WebMessageTest = WebLayerBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(WebMessageTest, SendAndReceive) {
+ EXPECT_TRUE(embedded_test_server()->Start());
+
+ base::RunLoop run_loop;
+ shell()->tab()->AddWebMessageHostFactory(
+ std::make_unique<WebMessageHostFactoryImpl>(run_loop.QuitClosure()),
+ base::ASCIIToUTF16("x"), {"*"});
+
+ // web_message_test.html posts a message immediately.
+ shell()->tab()->GetNavigationController()->Navigate(
+ embedded_test_server()->GetURL("/web_message_test.html"));
+ run_loop.Run();
+
+ // There should be two messages. The one from the page, and the ack triggered
+ // when WebMessageHostImpl calls PostMessage().
+ ASSERT_TRUE(current_connection);
+ ASSERT_EQ(2u, current_connection->messages().size());
+ EXPECT_EQ(base::ASCIIToUTF16("from page"), current_connection->messages()[0]);
+ EXPECT_EQ(base::ASCIIToUTF16("bouncing from c++"),
+ current_connection->messages()[1]);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.cc b/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.cc
new file mode 100644
index 00000000000..e634cd95d78
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.cc
@@ -0,0 +1,25 @@
+// 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.
+
+#include "weblayer/browser/js_communication/web_message_host_factory_proxy.h"
+
+#include "weblayer/browser/js_communication/web_message_reply_proxy_impl.h"
+
+namespace weblayer {
+
+WebMessageHostFactoryProxy::WebMessageHostFactoryProxy(
+ const base::android::JavaParamRef<jobject>& client)
+ : client_(client) {}
+
+WebMessageHostFactoryProxy::~WebMessageHostFactoryProxy() = default;
+
+std::unique_ptr<WebMessageHost> WebMessageHostFactoryProxy::CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* proxy) {
+ return std::make_unique<WebMessageReplyProxyImpl>(
+ ++next_id_, client_, origin_string, is_main_frame, proxy);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.h b/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.h
new file mode 100644
index 00000000000..93e5d55d71e
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_host_factory_proxy.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_PROXY_H_
+#define WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_PROXY_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "weblayer/public/js_communication/web_message_host_factory.h"
+
+namespace weblayer {
+
+// TabImpl, on android, creates a WebMessageHostFactoryProxy for every call
+// to RegisterWebMessageCallback(). This is used to delegate the calls back to
+// the Java side.
+class WebMessageHostFactoryProxy : public WebMessageHostFactory {
+ public:
+ explicit WebMessageHostFactoryProxy(
+ const base::android::JavaParamRef<jobject>& client);
+ WebMessageHostFactoryProxy(const WebMessageHostFactoryProxy&) = delete;
+ WebMessageHostFactoryProxy& operator=(const WebMessageHostFactoryProxy&) =
+ delete;
+ ~WebMessageHostFactoryProxy() override;
+
+ // WebMessageHostFactory:
+ std::unique_ptr<WebMessageHost> CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* proxy) override;
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> client_;
+ int next_id_ = 0;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_PROXY_H_
diff --git a/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.cc b/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.cc
new file mode 100644
index 00000000000..7a6088f540b
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.cc
@@ -0,0 +1,70 @@
+// 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.
+
+#include "weblayer/browser/js_communication/web_message_host_factory_wrapper.h"
+
+#include "components/js_injection/browser/web_message.h"
+#include "components/js_injection/browser/web_message_host.h"
+#include "components/js_injection/browser/web_message_reply_proxy.h"
+#include "weblayer/public/js_communication/web_message.h"
+#include "weblayer/public/js_communication/web_message_host.h"
+#include "weblayer/public/js_communication/web_message_host_factory.h"
+#include "weblayer/public/js_communication/web_message_reply_proxy.h"
+
+namespace weblayer {
+namespace {
+
+// An implementation of js_injection::WebMessageHost that delegates to the
+// corresponding WebLayer type. This also serves as the WebMessageReplyProxy
+// implementation, which forwards to the js_injection implementation.
+class WebMessageHostWrapper : public js_injection::WebMessageHost,
+ public WebMessageReplyProxy {
+ public:
+ WebMessageHostWrapper(weblayer::WebMessageHostFactory* factory,
+ const std::string& origin_string,
+ bool is_main_frame,
+ js_injection::WebMessageReplyProxy* proxy)
+ : proxy_(proxy),
+ connection_(factory->CreateHost(origin_string, is_main_frame, this)) {}
+
+ // js_injection::WebMessageHost:
+ void OnPostMessage(
+ std::unique_ptr<js_injection::WebMessage> message) override {
+ std::unique_ptr<WebMessage> m = std::make_unique<WebMessage>();
+ m->message = message->message;
+ connection_->OnPostMessage(std::move(m));
+ }
+
+ // WebMessageReplyProxy:
+ void PostMessage(std::unique_ptr<WebMessage> message) override {
+ std::unique_ptr<js_injection::WebMessage> w =
+ std::make_unique<js_injection::WebMessage>();
+ w->message = std::move(message->message);
+ proxy_->PostMessage(std::move(w));
+ }
+
+ private:
+ js_injection::WebMessageReplyProxy* proxy_;
+ std::unique_ptr<weblayer::WebMessageHost> connection_;
+};
+
+} // namespace
+
+WebMessageHostFactoryWrapper::WebMessageHostFactoryWrapper(
+ std::unique_ptr<weblayer::WebMessageHostFactory> factory)
+ : factory_(std::move(factory)) {}
+
+WebMessageHostFactoryWrapper::~WebMessageHostFactoryWrapper() = default;
+
+std::unique_ptr<js_injection::WebMessageHost>
+WebMessageHostFactoryWrapper::CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ js_injection::WebMessageReplyProxy* proxy) {
+ auto wrapper = std::make_unique<WebMessageHostWrapper>(
+ factory_.get(), origin_string, is_main_frame, proxy);
+ return wrapper;
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.h b/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.h
new file mode 100644
index 00000000000..1c32d02bfc2
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_host_factory_wrapper.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_WRAPPER_H_
+#define WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_WRAPPER_H_
+
+#include "components/js_injection/browser/web_message_host_factory.h"
+
+namespace weblayer {
+
+class WebMessageHostFactory;
+
+// Provides an implementation of js_injection::WebMessageHostFactory that
+// wraps the corresponding WebLayer type.
+class WebMessageHostFactoryWrapper
+ : public js_injection::WebMessageHostFactory {
+ public:
+ explicit WebMessageHostFactoryWrapper(
+ std::unique_ptr<weblayer::WebMessageHostFactory> factory);
+ WebMessageHostFactoryWrapper(const WebMessageHostFactoryWrapper&) = delete;
+ WebMessageHostFactoryWrapper& operator=(const WebMessageHostFactoryWrapper&) =
+ delete;
+ ~WebMessageHostFactoryWrapper() override;
+
+ // js_injection::WebMessageHostFactory:
+ std::unique_ptr<js_injection::WebMessageHost> CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ js_injection::WebMessageReplyProxy* proxy) override;
+
+ private:
+ std::unique_ptr<weblayer::WebMessageHostFactory> factory_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_WRAPPER_H_
diff --git a/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.cc b/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.cc
new file mode 100644
index 00000000000..8172852ecec
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.cc
@@ -0,0 +1,52 @@
+// 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.
+
+#include "weblayer/browser/js_communication/web_message_reply_proxy_impl.h"
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "weblayer/browser/java/jni/WebMessageReplyProxyImpl_jni.h"
+#include "weblayer/public/js_communication/web_message.h"
+#include "weblayer/public/js_communication/web_message_reply_proxy.h"
+
+namespace weblayer {
+
+WebMessageReplyProxyImpl::WebMessageReplyProxyImpl(
+ int id,
+ base::android::ScopedJavaGlobalRef<jobject> client,
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* reply_proxy)
+ : reply_proxy_(reply_proxy) {
+ auto* env = base::android::AttachCurrentThread();
+ java_object_ = Java_WebMessageReplyProxyImpl_create(
+ env, reinterpret_cast<intptr_t>(this), id, client, is_main_frame,
+ base::android::ConvertUTF8ToJavaString(env, origin_string));
+}
+
+WebMessageReplyProxyImpl::~WebMessageReplyProxyImpl() {
+ Java_WebMessageReplyProxyImpl_onNativeDestroyed(
+ base::android::AttachCurrentThread(), java_object_);
+}
+
+void WebMessageReplyProxyImpl::PostMessage(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& message_contents) {
+ auto message = std::make_unique<WebMessage>();
+ base::android::ConvertJavaStringToUTF16(env, message_contents,
+ &(message->message));
+ reply_proxy_->PostMessage(std::move(message));
+}
+
+void WebMessageReplyProxyImpl::OnPostMessage(
+ std::unique_ptr<WebMessage> message) {
+ auto* env = base::android::AttachCurrentThread();
+ Java_WebMessageReplyProxyImpl_onPostMessage(
+ env, java_object_,
+ base::android::ConvertUTF16ToJavaString(env, message->message));
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.h b/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.h
new file mode 100644
index 00000000000..dbcfe016f06
--- /dev/null
+++ b/chromium/weblayer/browser/js_communication/web_message_reply_proxy_impl.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_IMPL_H_
+#define WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_IMPL_H_
+
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "components/js_injection/browser/web_message_host.h"
+#include "weblayer/public/js_communication/web_message_host.h"
+
+namespace weblayer {
+
+class WebMessageReplyProxy;
+
+// Created only on the Android side to support post-message.
+// WebMessageReplyProxyImpl creates the Java WebMessageReplyProxy that is then
+// sent over to the client side for communication with the page.
+class WebMessageReplyProxyImpl : public WebMessageHost {
+ public:
+ WebMessageReplyProxyImpl(int id,
+ base::android::ScopedJavaGlobalRef<jobject> client,
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* reply_proxy);
+ WebMessageReplyProxyImpl(const WebMessageReplyProxyImpl&) = delete;
+ WebMessageReplyProxyImpl& operator=(const WebMessageReplyProxyImpl&) = delete;
+ ~WebMessageReplyProxyImpl() override;
+
+ void PostMessage(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& message_contents);
+ // WebMessageHost:
+ void OnPostMessage(std::unique_ptr<WebMessage> message) override;
+
+ private:
+ WebMessageReplyProxy* reply_proxy_;
+
+ // The Java WebMessageReplyProxy.
+ base::android::ScopedJavaGlobalRef<jobject> java_object_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_IMPL_H_
diff --git a/chromium/weblayer/browser/navigation_browsertest.cc b/chromium/weblayer/browser/navigation_browsertest.cc
index 73773c8b092..c573269cc60 100644
--- a/chromium/weblayer/browser/navigation_browsertest.cc
+++ b/chromium/weblayer/browser/navigation_browsertest.cc
@@ -361,6 +361,28 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeader) {
EXPECT_EQ(header_value, response_2.http_request()->headers.at(header_name));
}
+// Verifies setting the 'referer' via SetRequestHeader() works as expected.
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeaderWithReferer) {
+ net::test_server::ControllableHttpResponse response(embedded_test_server(),
+ "", true);
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ const std::string header_name = "Referer";
+ const std::string header_value = "http://request.com";
+ NavigationObserverImpl observer(GetNavigationController());
+ observer.SetStartedCallback(
+ base::BindLambdaForTesting([&](Navigation* navigation) {
+ navigation->SetRequestHeader(header_name, header_value);
+ }));
+
+ shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html"));
+ response.WaitForRequest();
+
+ // Verify 'referer' matches expected value.
+ EXPECT_EQ(GURL(header_value),
+ GURL(response.http_request()->headers.at(header_name)));
+}
+
IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeaderInRedirect) {
net::test_server::ControllableHttpResponse response_1(embedded_test_server(),
"", true);
diff --git a/chromium/weblayer/browser/navigation_controller_impl.cc b/chromium/weblayer/browser/navigation_controller_impl.cc
index 6b9b38abadd..cabbd2cca24 100644
--- a/chromium/weblayer/browser/navigation_controller_impl.cc
+++ b/chromium/weblayer/browser/navigation_controller_impl.cc
@@ -4,6 +4,8 @@
#include "weblayer/browser/navigation_controller_impl.h"
+#include <utility>
+
#include "base/auto_reset.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -145,6 +147,11 @@ ScopedJavaLocalRef<jstring> NavigationControllerImpl::GetNavigationEntryTitle(
return ScopedJavaLocalRef<jstring>(base::android::ConvertUTF8ToJavaString(
env, GetNavigationEntryTitle(index)));
}
+
+bool NavigationControllerImpl::IsNavigationEntrySkippable(JNIEnv* env,
+ int index) {
+ return IsNavigationEntrySkippable(index);
+}
#endif
void NavigationControllerImpl::WillRedirectRequest(
@@ -257,6 +264,10 @@ std::string NavigationControllerImpl::GetNavigationEntryTitle(int index) {
return base::UTF16ToUTF8(entry->GetTitle());
}
+bool NavigationControllerImpl::IsNavigationEntrySkippable(int index) {
+ return web_contents()->GetController().IsEntryMarkedToBeSkipped(index);
+}
+
void NavigationControllerImpl::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
@@ -353,6 +364,14 @@ void NavigationControllerImpl::DidFinishNavigation(
observer.NavigationFailed(navigation);
}
+ // Note InsertVisualStateCallback currently does not take into account
+ // any delays from surface sync, ie a frame submitted by renderer may not
+ // be displayed immediately. Such situations should be rare however, so
+ // this should be good enough for the purposes needed.
+ web_contents()->GetMainFrame()->InsertVisualStateCallback(base::BindOnce(
+ &NavigationControllerImpl::OldPageNoLongerRendered,
+ weak_ptr_factory_.GetWeakPtr(), navigation_handle->GetURL()));
+
navigation_map_.erase(navigation_map_.find(navigation_handle));
}
@@ -389,6 +408,20 @@ void NavigationControllerImpl::DidFirstVisuallyNonEmptyPaint() {
observer.OnFirstContentfulPaint();
}
+void NavigationControllerImpl::OldPageNoLongerRendered(const GURL& url,
+ bool success) {
+#if defined(OS_ANDROID)
+ TRACE_EVENT0("weblayer",
+ "Java_NavigationControllerImpl_onOldPageNoLongerRendered");
+ JNIEnv* env = AttachCurrentThread();
+ Java_NavigationControllerImpl_onOldPageNoLongerRendered(
+ env, java_controller_,
+ base::android::ConvertUTF8ToJavaString(env, url.spec()));
+#endif
+ for (auto& observer : observers_)
+ observer.OnOldPageNoLongerRendered(url);
+}
+
void NavigationControllerImpl::NotifyLoadStateChanged() {
#if defined(OS_ANDROID)
if (java_controller_) {
diff --git a/chromium/weblayer/browser/navigation_controller_impl.h b/chromium/weblayer/browser/navigation_controller_impl.h
index 52dd02735cb..ce96e147504 100644
--- a/chromium/weblayer/browser/navigation_controller_impl.h
+++ b/chromium/weblayer/browser/navigation_controller_impl.h
@@ -9,6 +9,7 @@
#include <memory>
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "build/build_config.h"
#include "content/public/browser/navigation_controller.h"
@@ -64,6 +65,7 @@ class NavigationControllerImpl : public NavigationController,
base::android::ScopedJavaLocalRef<jstring> GetNavigationEntryTitle(
JNIEnv* env,
int index);
+ bool IsNavigationEntrySkippable(JNIEnv* env, int index);
#endif
private:
@@ -90,6 +92,7 @@ class NavigationControllerImpl : public NavigationController,
int GetNavigationListCurrentIndex() override;
GURL GetNavigationEntryDisplayURL(int index) override;
std::string GetNavigationEntryTitle(int index) override;
+ bool IsNavigationEntrySkippable(int index) override;
// content::WebContentsObserver implementation:
void DidStartNavigation(
@@ -105,6 +108,7 @@ class NavigationControllerImpl : public NavigationController,
void LoadProgressChanged(double progress) override;
void DidFirstVisuallyNonEmptyPaint() override;
+ void OldPageNoLongerRendered(const GURL& url, bool success);
void NotifyLoadStateChanged();
void DoNavigate(
@@ -125,6 +129,8 @@ class NavigationControllerImpl : public NavigationController,
base::android::ScopedJavaGlobalRef<jobject> java_controller_;
#endif
+ base::WeakPtrFactory<NavigationControllerImpl> weak_ptr_factory_{this};
+
DISALLOW_COPY_AND_ASSIGN(NavigationControllerImpl);
};
diff --git a/chromium/weblayer/browser/navigation_impl.cc b/chromium/weblayer/browser/navigation_impl.cc
index 211f2f0e877..e160069f225 100644
--- a/chromium/weblayer/browser/navigation_impl.cc
+++ b/chromium/weblayer/browser/navigation_impl.cc
@@ -9,6 +9,7 @@
#include "net/base/net_errors.h"
#include "net/http/http_util.h"
#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+#include "third_party/blink/public/mojom/referrer.mojom.h"
#if defined(OS_ANDROID)
#include "base/android/jni_array.h"
@@ -150,8 +151,17 @@ Navigation::LoadError NavigationImpl::GetLoadError() {
void NavigationImpl::SetRequestHeader(const std::string& name,
const std::string& value) {
- // Any headers coming from the client should be exempt from CORS checks.
- navigation_handle_->SetCorsExemptRequestHeader(name, value);
+ if (base::ToLowerASCII(name) == "referer") {
+ // The referrer needs to be special cased as content maintains it
+ // separately.
+ auto referrer = blink::mojom::Referrer::New();
+ referrer->url = GURL(value);
+ referrer->policy = network::mojom::ReferrerPolicy::kDefault;
+ navigation_handle_->SetReferrer(std::move(referrer));
+ } else {
+ // Any headers coming from the client should be exempt from CORS checks.
+ navigation_handle_->SetCorsExemptRequestHeader(name, value);
+ }
}
void NavigationImpl::SetUserAgentString(const std::string& value) {
diff --git a/chromium/weblayer/browser/new_tab_callback_proxy.cc b/chromium/weblayer/browser/new_tab_callback_proxy.cc
index 8e466430c06..b991a8abfe0 100644
--- a/chromium/weblayer/browser/new_tab_callback_proxy.cc
+++ b/chromium/weblayer/browser/new_tab_callback_proxy.cc
@@ -23,12 +23,12 @@ NewTabCallbackProxy::~NewTabCallbackProxy() {
tab_->SetNewTabDelegate(nullptr);
}
-void NewTabCallbackProxy::OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) {
+void NewTabCallbackProxy::OnNewTab(Tab* tab, NewTabType type) {
JNIEnv* env = AttachCurrentThread();
// The Java side takes ownership of Tab.
TRACE_EVENT0("weblayer", "Java_NewTabCallbackProxy_onNewTab");
Java_NewTabCallbackProxy_onNewTab(env, java_impl_,
- reinterpret_cast<jlong>(tab.release()),
+ static_cast<TabImpl*>(tab)->GetJavaTab(),
static_cast<int>(type));
}
diff --git a/chromium/weblayer/browser/new_tab_callback_proxy.h b/chromium/weblayer/browser/new_tab_callback_proxy.h
index 500d2f81894..02667f48607 100644
--- a/chromium/weblayer/browser/new_tab_callback_proxy.h
+++ b/chromium/weblayer/browser/new_tab_callback_proxy.h
@@ -23,7 +23,7 @@ class NewTabCallbackProxy : public NewTabDelegate {
~NewTabCallbackProxy() override;
// NewTabDelegate:
- void OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) override;
+ void OnNewTab(Tab* tab, NewTabType type) override;
void CloseTab() override;
private:
diff --git a/chromium/weblayer/browser/password_manager_driver_factory.cc b/chromium/weblayer/browser/password_manager_driver_factory.cc
new file mode 100644
index 00000000000..8bdf396a44e
--- /dev/null
+++ b/chromium/weblayer/browser/password_manager_driver_factory.cc
@@ -0,0 +1,132 @@
+// 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.
+
+#include "weblayer/browser/password_manager_driver_factory.h"
+
+#include "components/password_manager/content/browser/bad_message.h"
+#include "components/site_isolation/site_isolation_policy.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+
+namespace weblayer {
+
+// A minimal implementation of autofill::mojom::PasswordManagerDriver which just
+// listens for the user to type into a password field.
+class PasswordManagerDriverFactory::PasswordManagerDriver
+ : public autofill::mojom::PasswordManagerDriver {
+ public:
+ explicit PasswordManagerDriver(content::RenderFrameHost* render_frame_host)
+ : render_frame_host_(render_frame_host) {}
+
+ void BindPendingReceiver(
+ mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
+ pending_receiver) {
+ password_manager_receiver_.Bind(std::move(pending_receiver));
+ }
+
+ private:
+ // autofill::mojom::PasswordManagerDriver:
+ // Note that these messages received from a potentially compromised renderer.
+ // For that reason, any access to form data should be validated via
+ // bad_message::CheckChildProcessSecurityPolicy.
+ void PasswordFormsParsed(
+ const std::vector<autofill::FormData>& forms_data) override {}
+ void PasswordFormsRendered(
+ const std::vector<autofill::FormData>& visible_forms_data,
+ bool did_stop_loading) override {}
+ void PasswordFormSubmitted(const autofill::FormData& form_data) override {}
+ void ShowManualFallbackForSaving(
+ const autofill::FormData& form_data) override {
+ if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
+ render_frame_host_, form_data.url,
+ password_manager::BadMessageReason::
+ CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING)) {
+ return;
+ }
+
+ if (site_isolation::SiteIsolationPolicy::
+ IsIsolationForPasswordSitesEnabled()) {
+ // This function signals that a password field has been filled (whether by
+ // the user, JS, autofill, or some other means) or a password form has
+ // been submitted. Use this as a heuristic to start site-isolating the
+ // form's site. This is intended to be used primarily when full site
+ // isolation is not used, such as on Android.
+ content::SiteInstance::StartIsolatingSite(
+ render_frame_host_->GetSiteInstance()->GetBrowserContext(),
+ form_data.url);
+ }
+ }
+ void HideManualFallbackForSaving() override {}
+ void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent
+ submission_indication_event) override {}
+ void RecordSavePasswordProgress(const std::string& log) override {}
+ void UserModifiedPasswordField() override {}
+ void UserModifiedNonPasswordField(autofill::FieldRendererId renderer_id,
+ const base::string16& value) override {}
+ void ShowPasswordSuggestions(base::i18n::TextDirection text_direction,
+ const base::string16& typed_username,
+ int options,
+ const gfx::RectF& bounds) override {}
+ void ShowTouchToFill() override {}
+ void CheckSafeBrowsingReputation(const GURL& form_action,
+ const GURL& frame_url) override {}
+ void FocusedInputChanged(
+ autofill::mojom::FocusedFieldType focused_field_type) override {}
+ void LogFirstFillingResult(autofill::FormRendererId form_renderer_id,
+ int32_t result) override {}
+
+ mojo::AssociatedReceiver<autofill::mojom::PasswordManagerDriver>
+ password_manager_receiver_{this};
+ content::RenderFrameHost* render_frame_host_;
+};
+
+PasswordManagerDriverFactory::PasswordManagerDriverFactory(
+ content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents) {}
+
+PasswordManagerDriverFactory::~PasswordManagerDriverFactory() = default;
+
+// static
+void PasswordManagerDriverFactory::BindPasswordManagerDriver(
+ mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
+ pending_receiver,
+ content::RenderFrameHost* render_frame_host) {
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderFrameHost(render_frame_host);
+ if (!web_contents)
+ return;
+
+ PasswordManagerDriverFactory* factory =
+ PasswordManagerDriverFactory::FromWebContents(web_contents);
+ if (!factory)
+ return;
+
+ factory->GetDriverForFrame(render_frame_host)
+ ->BindPendingReceiver(std::move(pending_receiver));
+}
+
+PasswordManagerDriverFactory::PasswordManagerDriver*
+PasswordManagerDriverFactory::GetDriverForFrame(
+ content::RenderFrameHost* render_frame_host) {
+ DCHECK_EQ(web_contents(),
+ content::WebContents::FromRenderFrameHost(render_frame_host));
+ DCHECK(render_frame_host->IsRenderFrameCreated());
+
+ // TryEmplace() will return an iterator to the driver corresponding to
+ // `render_frame_host`. It creates a new one if required.
+ return &base::TryEmplace(frame_driver_map_, render_frame_host,
+ render_frame_host)
+ .first->second;
+}
+
+void PasswordManagerDriverFactory::RenderFrameDeleted(
+ content::RenderFrameHost* render_frame_host) {
+ frame_driver_map_.erase(render_frame_host);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(PasswordManagerDriverFactory)
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/password_manager_driver_factory.h b/chromium/weblayer/browser/password_manager_driver_factory.h
new file mode 100644
index 00000000000..b29d46960c4
--- /dev/null
+++ b/chromium/weblayer/browser/password_manager_driver_factory.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_PASSWORD_MANAGER_DRIVER_FACTORY_H_
+#define WEBLAYER_BROWSER_PASSWORD_MANAGER_DRIVER_FACTORY_H_
+
+#include <map>
+
+#include "base/supports_user_data.h"
+#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace weblayer {
+
+// WebLayer uses the system autofill and not the autofill used by Chrome. This
+// factory and the corresponding driver are only used to listen for the
+// notification that a password was typed into a form, since this is used as a
+// signal to start isolating that site.
+// TODO(crbug.com/1088446): Find a way to easily share this with Chrome.
+class PasswordManagerDriverFactory
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<PasswordManagerDriverFactory> {
+ public:
+ ~PasswordManagerDriverFactory() override;
+
+ PasswordManagerDriverFactory(const PasswordManagerDriverFactory&) = delete;
+ PasswordManagerDriverFactory& operator=(const PasswordManagerDriverFactory&) =
+ delete;
+
+ static void BindPasswordManagerDriver(
+ mojo::PendingAssociatedReceiver<autofill::mojom::PasswordManagerDriver>
+ pending_receiver,
+ content::RenderFrameHost* render_frame_host);
+
+ private:
+ class PasswordManagerDriver;
+ friend class content::WebContentsUserData<PasswordManagerDriverFactory>;
+
+ explicit PasswordManagerDriverFactory(content::WebContents* web_contents);
+
+ // content::WebContentsObserver:
+ void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+
+ PasswordManagerDriver* GetDriverForFrame(
+ content::RenderFrameHost* render_frame_host);
+
+ std::map<content::RenderFrameHost*, PasswordManagerDriver> frame_driver_map_;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_PASSWORD_MANAGER_DRIVER_FACTORY_H_
diff --git a/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc b/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc
index 9b27ab1d974..e2fb5ff077e 100644
--- a/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc
+++ b/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc
@@ -4,6 +4,8 @@
#include "weblayer/browser/permissions/weblayer_permissions_client.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "weblayer/browser/cookie_settings_factory.h"
#include "weblayer/browser/host_content_settings_map_factory.h"
#include "weblayer/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
@@ -26,6 +28,12 @@ HostContentSettingsMap* WebLayerPermissionsClient::GetSettingsMap(
return HostContentSettingsMapFactory::GetForBrowserContext(browser_context);
}
+scoped_refptr<content_settings::CookieSettings>
+WebLayerPermissionsClient::GetCookieSettings(
+ content::BrowserContext* browser_context) {
+ return CookieSettingsFactory::GetForBrowserContext(browser_context);
+}
+
permissions::PermissionDecisionAutoBlocker*
WebLayerPermissionsClient::GetPermissionDecisionAutoBlocker(
content::BrowserContext* browser_context) {
diff --git a/chromium/weblayer/browser/permissions/weblayer_permissions_client.h b/chromium/weblayer/browser/permissions/weblayer_permissions_client.h
index 1d7f899a89a..8bcc5dbc215 100644
--- a/chromium/weblayer/browser/permissions/weblayer_permissions_client.h
+++ b/chromium/weblayer/browser/permissions/weblayer_permissions_client.h
@@ -22,6 +22,8 @@ class WebLayerPermissionsClient : public permissions::PermissionsClient {
// PermissionsClient:
HostContentSettingsMap* GetSettingsMap(
content::BrowserContext* browser_context) override;
+ scoped_refptr<content_settings::CookieSettings> GetCookieSettings(
+ content::BrowserContext* browser_context) override;
permissions::PermissionDecisionAutoBlocker* GetPermissionDecisionAutoBlocker(
content::BrowserContext* browser_context) override;
permissions::PermissionManager* GetPermissionManager(
diff --git a/chromium/weblayer/browser/persistence/browser_persistence_common.cc b/chromium/weblayer/browser/persistence/browser_persistence_common.cc
index 76e2d4ce630..9ca03a353d2 100644
--- a/chromium/weblayer/browser/persistence/browser_persistence_common.cc
+++ b/chromium/weblayer/browser/persistence/browser_persistence_common.cc
@@ -9,11 +9,11 @@
#include "components/sessions/core/session_command.h"
#include "components/sessions/core/session_service_commands.h"
#include "components/sessions/core/session_types.h"
-#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_url_handler.h"
#include "content/public/browser/dom_storage_context.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/storage_partition.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
@@ -84,6 +84,7 @@ void ProcessRestoreCommands(
DCHECK(entries.empty());
TabImpl* tab = browser->CreateTabForSessionRestore(std::move(web_contents),
session_tab.guid);
+ tab->SetData(session_tab.data);
if (!had_tabs && i == (windows[0])->selected_tab_index)
browser->SetActiveTab(tab);
@@ -142,6 +143,8 @@ BuildCommandsForTabConfiguration(const SessionID& browser_session_id,
result.push_back(sessions::CreateSetTabGuidCommand(tab_id, tab->GetGuid()));
+ result.push_back(sessions::CreateSetTabDataCommand(tab_id, tab->GetData()));
+
return result;
}
diff --git a/chromium/weblayer/browser/persistence/browser_persister.cc b/chromium/weblayer/browser/persistence/browser_persister.cc
index c7fe1a5c325..fac791261ab 100644
--- a/chromium/weblayer/browser/persistence/browser_persister.cc
+++ b/chromium/weblayer/browser/persistence/browser_persister.cc
@@ -18,13 +18,13 @@
#include "components/sessions/core/session_constants.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
-#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/restore_type.h"
#include "content/public/browser/session_storage_namespace.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/persistence/browser_persistence_common.h"
#include "weblayer/browser/profile_impl.h"
@@ -105,8 +105,9 @@ void BrowserPersister::OnGeneratedNewCryptoKey(
}
void BrowserPersister::OnTabAdded(Tab* tab) {
- content::WebContents* web_contents =
- static_cast<TabImpl*>(tab)->web_contents();
+ auto* tab_impl = static_cast<TabImpl*>(tab);
+ data_observer_.Add(tab_impl);
+ content::WebContents* web_contents = tab_impl->web_contents();
auto* tab_helper = sessions::SessionTabHelper::FromWebContents(web_contents);
DCHECK(tab_helper);
tab_helper->SetWindowID(browser_session_id_);
@@ -130,10 +131,11 @@ void BrowserPersister::OnTabAdded(Tab* tab) {
}
void BrowserPersister::OnTabRemoved(Tab* tab, bool active_tab_changed) {
+ auto* tab_impl = static_cast<TabImpl*>(tab);
+ data_observer_.Remove(tab_impl);
// Allow the associated sessionStorage to get deleted; it won't be needed
// in the session restore.
- content::WebContents* web_contents =
- static_cast<TabImpl*>(tab)->web_contents();
+ content::WebContents* web_contents = tab_impl->web_contents();
content::SessionStorageNamespace* session_storage_namespace =
web_contents->GetController().GetDefaultSessionStorageNamespace();
session_storage_namespace->SetShouldPersist(false);
@@ -161,6 +163,16 @@ void BrowserPersister::OnActiveTabChanged(Tab* tab) {
browser_session_id_, index));
}
+void BrowserPersister::OnDataChanged(
+ TabImpl* tab,
+ const std::map<std::string, std::string>& data) {
+ if (rebuild_on_next_save_)
+ return;
+
+ ScheduleCommand(
+ sessions::CreateSetTabDataCommand(GetSessionIDForTab(tab), data));
+}
+
void BrowserPersister::SetTabUserAgentOverride(
const SessionID& window_id,
const SessionID& tab_id,
diff --git a/chromium/weblayer/browser/persistence/browser_persister.h b/chromium/weblayer/browser/persistence/browser_persister.h
index c157abec93e..a956914ad59 100644
--- a/chromium/weblayer/browser/persistence/browser_persister.h
+++ b/chromium/weblayer/browser/persistence/browser_persister.h
@@ -14,10 +14,12 @@
#include <vector>
#include "base/macros.h"
+#include "base/scoped_observer.h"
#include "base/task/cancelable_task_tracker.h"
#include "components/sessions/content/session_tab_helper_delegate.h"
#include "components/sessions/core/command_storage_manager_delegate.h"
#include "components/sessions/core/session_service_commands.h"
+#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/browser_observer.h"
class SessionID;
@@ -29,7 +31,6 @@ class SessionCommand;
namespace weblayer {
class BrowserImpl;
-class TabImpl;
// BrowserPersister is responsible for maintaining the state of tabs in a
// single Browser so that they can be restored at a later date. The state is
@@ -40,7 +41,8 @@ class TabImpl;
// current state.
class BrowserPersister : public sessions::CommandStorageManagerDelegate,
public sessions::SessionTabHelperDelegate,
- public BrowserObserver {
+ public BrowserObserver,
+ public TabImpl::DataObserver {
public:
BrowserPersister(const base::FilePath& path,
BrowserImpl* browser,
@@ -72,6 +74,10 @@ class BrowserPersister : public sessions::CommandStorageManagerDelegate,
void OnTabRemoved(Tab* tab, bool active_tab_changed) override;
void OnActiveTabChanged(Tab* tab) override;
+ // TabImpl::DataObserver:
+ void OnDataChanged(TabImpl* tab,
+ const std::map<std::string, std::string>& data) override;
+
// sessions::SessionTabHelperDelegate:
void SetTabUserAgentOverride(const SessionID& window_id,
const SessionID& tab_id,
@@ -127,6 +133,12 @@ class BrowserPersister : public sessions::CommandStorageManagerDelegate,
std::vector<uint8_t> crypto_key_;
+ ScopedObserver<TabImpl,
+ TabImpl::DataObserver,
+ &TabImpl::AddDataObserver,
+ &TabImpl::RemoveDataObserver>
+ data_observer_{this};
+
base::CancelableTaskTracker cancelable_task_tracker_;
};
diff --git a/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc b/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc
index 57933cd8cbe..23e30e3217c 100644
--- a/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc
+++ b/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc
@@ -6,16 +6,21 @@
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/guid.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "components/sessions/core/command_storage_manager_test_helper.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "net/base/filename_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/common/weblayer_paths.h"
@@ -40,6 +45,7 @@ class BrowserPersisterTestHelper {
};
namespace {
+using testing::UnorderedElementsAre;
class OneShotNavigationObserver : public NavigationObserver {
public:
@@ -183,7 +189,7 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, SingleTab) {
ASSERT_TRUE(embedded_test_server()->Start());
std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
- Tab* tab = browser->AddTab(Tab::Create(GetProfile()));
+ Tab* tab = browser->CreateTab();
const GURL url = embedded_test_server()->GetURL("/simple_page.html");
NavigateAndWaitForCompletion(url, tab);
ShutdownBrowserPersisterAndWait(browser.get());
@@ -208,7 +214,7 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, RestoresGuid) {
ASSERT_TRUE(embedded_test_server()->Start());
std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
- Tab* tab = browser->AddTab(Tab::Create(GetProfile()));
+ Tab* tab = browser->CreateTab();
const std::string original_guid = tab->GetGuid();
EXPECT_FALSE(original_guid.empty());
EXPECT_TRUE(base::IsValidGUID(original_guid));
@@ -230,15 +236,72 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, RestoresGuid) {
EXPECT_EQ(original_guid, browser->GetTabs()[0]->GetGuid());
}
+IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, RestoresData) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
+ Tab* tab = browser->CreateTab();
+ tab->SetData({{"abc", "efg"}});
+ const GURL url = embedded_test_server()->GetURL("/simple_page.html");
+ NavigateAndWaitForCompletion(url, tab);
+ ShutdownBrowserPersisterAndWait(browser.get());
+ tab = nullptr;
+ browser.reset();
+
+ browser = CreateBrowser(GetProfile(), "x");
+ // Should be no tabs while waiting for restore.
+ EXPECT_TRUE(browser->GetTabs().empty());
+ // Wait for the restore and navigation to complete.
+ BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
+ browser.get(), url);
+
+ ASSERT_EQ(1u, browser->GetTabs().size());
+ EXPECT_EQ(browser->GetTabs()[0], browser->GetActiveTab());
+ EXPECT_THAT(browser->GetTabs()[0]->GetData(),
+ UnorderedElementsAre(std::make_pair("abc", "efg")));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, RestoresMostRecentData) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
+ Tab* tab = browser->CreateTab();
+ tab->SetData({{"xxx", "xxx"}});
+ const GURL url = embedded_test_server()->GetURL("/simple_page.html");
+ NavigateAndWaitForCompletion(url, tab);
+
+ // Make sure the data has been saved, then set different data on the tab.
+ BrowserPersisterTestHelper::GetCommandStorageManager(
+ browser->browser_persister())
+ ->Save();
+ tab->SetData({{"abc", "efg"}});
+
+ ShutdownBrowserPersisterAndWait(browser.get());
+ tab = nullptr;
+ browser.reset();
+
+ browser = CreateBrowser(GetProfile(), "x");
+ // Should be no tabs while waiting for restore.
+ EXPECT_TRUE(browser->GetTabs().empty());
+ // Wait for the restore and navigation to complete.
+ BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
+ browser.get(), url);
+
+ ASSERT_EQ(1u, browser->GetTabs().size());
+ EXPECT_EQ(browser->GetTabs()[0], browser->GetActiveTab());
+ EXPECT_THAT(browser->GetTabs()[0]->GetData(),
+ UnorderedElementsAre(std::make_pair("abc", "efg")));
+}
+
IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, TwoTabs) {
ASSERT_TRUE(embedded_test_server()->Start());
std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
- Tab* tab1 = browser->AddTab(Tab::Create(GetProfile()));
+ Tab* tab1 = browser->CreateTab();
const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
NavigateAndWaitForCompletion(url1, tab1);
- Tab* tab2 = browser->AddTab(Tab::Create(GetProfile()));
+ Tab* tab2 = browser->CreateTab();
const GURL url2 = embedded_test_server()->GetURL("/simple_page2.html");
NavigateAndWaitForCompletion(url2, tab2);
browser->SetActiveTab(tab2);
@@ -282,23 +345,23 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, MoveBetweenBrowsers) {
// Create a browser with two tabs.
std::unique_ptr<BrowserImpl> browser1 = CreateBrowser(GetProfile(), "x");
- Tab* tab1 = browser1->AddTab(Tab::Create(GetProfile()));
+ Tab* tab1 = browser1->CreateTab();
const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
NavigateAndWaitForCompletion(url1, tab1);
- Tab* tab2 = browser1->AddTab(Tab::Create(GetProfile()));
+ Tab* tab2 = browser1->CreateTab();
const GURL url2 = embedded_test_server()->GetURL("/simple_page2.html");
NavigateAndWaitForCompletion(url2, tab2);
browser1->SetActiveTab(tab2);
// Create another browser with a single tab.
std::unique_ptr<BrowserImpl> browser2 = CreateBrowser(GetProfile(), "y");
- Tab* tab3 = browser2->AddTab(Tab::Create(GetProfile()));
+ Tab* tab3 = browser2->CreateTab();
const GURL url3 = embedded_test_server()->GetURL("/simple_page3.html");
NavigateAndWaitForCompletion(url3, tab3);
// Move |tab2| to |browser2|.
- browser2->AddTab(browser1->RemoveTab(tab2));
+ browser2->AddTab(tab2);
browser2->SetActiveTab(tab2);
ShutdownBrowserPersisterAndWait(browser1.get());
@@ -333,4 +396,81 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, MoveBetweenBrowsers) {
content::WaitForLoadStop(restored_tab_3->web_contents());
}
+class BrowserPersisterTestWithTwoPersistedIds : public WebLayerBrowserTest {
+ public:
+ // WebLayerBrowserTest:
+ void SetUpOnMainThread() override {
+ WebLayerBrowserTest::SetUpOnMainThread();
+ // Configure two browsers with ids 'x' and 'y'.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ std::unique_ptr<BrowserImpl> browser1 = CreateBrowser(GetProfile(), "x");
+ const GURL url1 = embedded_test_server()->GetURL("/simple_page.html");
+ NavigateAndWaitForCompletion(url1, browser1->CreateTab());
+
+ std::unique_ptr<BrowserImpl> browser2 = CreateBrowser(GetProfile(), "y");
+ const GURL url2 = embedded_test_server()->GetURL("/simple_page3.html");
+ NavigateAndWaitForCompletion(url2, browser2->CreateTab());
+
+ // Shut down the browsers.
+ ShutdownBrowserPersisterAndWait(browser1.get());
+ browser1.reset();
+ ShutdownBrowserPersisterAndWait(browser2.get());
+ browser2.reset();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(BrowserPersisterTestWithTwoPersistedIds,
+ GetBrowserPersistenceIds) {
+ {
+ // Create a file that has the name of a valid persistence file, but has
+ // invalid contents.
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ base::WriteFile(BuildPathForBrowserPersister(
+ GetProfile()->GetBrowserPersisterDataBaseDir(), "z"),
+ "a bogus persistence file");
+ }
+
+ base::RunLoop run_loop;
+ base::flat_set<std::string> persistence_ids;
+ GetProfile()->GetBrowserPersistenceIds(
+ base::BindLambdaForTesting([&](base::flat_set<std::string> ids) {
+ persistence_ids = std::move(ids);
+ run_loop.Quit();
+ }));
+ run_loop.Run();
+ ASSERT_EQ(2u, persistence_ids.size());
+ EXPECT_TRUE(persistence_ids.contains("x"));
+ EXPECT_TRUE(persistence_ids.contains("y"));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPersisterTestWithTwoPersistedIds,
+ RemoveBrowserPersistenceStorage) {
+ base::FilePath file_path1 = BuildPathForBrowserPersister(
+ GetProfile()->GetBrowserPersisterDataBaseDir(), "x");
+ base::FilePath file_path2 = BuildPathForBrowserPersister(
+ GetProfile()->GetBrowserPersisterDataBaseDir(), "y");
+
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ ASSERT_TRUE(base::PathExists(file_path1));
+ ASSERT_TRUE(base::PathExists(file_path2));
+ }
+ base::RunLoop run_loop;
+ base::flat_set<std::string> persistence_ids;
+ persistence_ids.insert("x");
+ persistence_ids.insert("y");
+ GetProfile()->RemoveBrowserPersistenceStorage(
+ base::BindLambdaForTesting([&](bool result) {
+ EXPECT_TRUE(result);
+ run_loop.Quit();
+ }),
+ std::move(persistence_ids));
+ run_loop.Run();
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ EXPECT_FALSE(base::PathExists(file_path1));
+ EXPECT_FALSE(base::PathExists(file_path2));
+ }
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc b/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc
new file mode 100644
index 00000000000..ead41f214f9
--- /dev/null
+++ b/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc
@@ -0,0 +1,91 @@
+// 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.
+
+#include "weblayer/browser/persistence/browser_persister_file_utils.h"
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/stl_util.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "components/base32/base32.h"
+#include "components/sessions/core/command_storage_backend.h"
+#include "content/public/browser/browser_thread.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/browser_list.h"
+#include "weblayer/browser/profile_impl.h"
+
+namespace weblayer {
+namespace {
+
+bool RemoveBrowserPersistenceStorageOnBackgroundThread(
+ const base::FilePath& path,
+ base::flat_set<std::string> ids) {
+ DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ bool all_succeeded = true;
+ for (const std::string& id : ids) {
+ DCHECK(!id.empty());
+ base::FilePath persistence_path = BuildPathForBrowserPersister(path, id);
+ if (!base::DeleteFile(persistence_path, /* recurse */ false))
+ all_succeeded = false;
+ }
+ return all_succeeded;
+}
+
+} // namespace
+
+base::flat_set<std::string> GetBrowserPersistenceIdsOnBackgroundThread(
+ const base::FilePath& path) {
+ DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ base::flat_set<std::string> ids;
+ base::FilePath matching_path = base::FilePath().AppendASCII(
+ std::string(BrowserImpl::kPersistenceFilePrefix) + std::string("*"));
+ base::FileEnumerator iter(path, /* recursive */ false,
+ base::FileEnumerator::FILES, matching_path.value());
+ for (base::FilePath name = iter.Next(); !name.empty(); name = iter.Next()) {
+ // The name is base32 encoded, which is ascii.
+ const std::string base_name = iter.GetInfo().GetName().MaybeAsASCII();
+ if (base_name.size() <= base::size(BrowserImpl::kPersistenceFilePrefix))
+ continue;
+
+ const std::string encoded_id =
+ base_name.substr(base::size(BrowserImpl::kPersistenceFilePrefix) - 1);
+ const std::string decoded_id = base32::Base32Decode(encoded_id);
+ if (!decoded_id.empty() &&
+ sessions::CommandStorageBackend::IsValidFile(name)) {
+ ids.insert(decoded_id);
+ }
+ }
+ return ids;
+}
+
+base::FilePath BuildPathForBrowserPersister(const base::FilePath& base_path,
+ const std::string& browser_id) {
+ DCHECK(!browser_id.empty());
+ const std::string encoded_name = base32::Base32Encode(browser_id);
+ return base_path.AppendASCII(BrowserImpl::kPersistenceFilePrefix +
+ encoded_name);
+}
+
+void RemoveBrowserPersistenceStorageImpl(
+ ProfileImpl* profile,
+ base::OnceCallback<void(bool)> done_callback,
+ base::flat_set<std::string> ids) {
+ // Remove any ids that are actively in use.
+ for (BrowserImpl* browser : BrowserList::GetInstance()->browsers()) {
+ if (browser->profile() == profile)
+ ids.erase(browser->GetPersistenceId());
+ }
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::BindOnce(&RemoveBrowserPersistenceStorageOnBackgroundThread,
+ profile->GetBrowserPersisterDataBaseDir(), std::move(ids)),
+ std::move(done_callback));
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/persistence/browser_persister_file_utils.h b/chromium/weblayer/browser/persistence/browser_persister_file_utils.h
new file mode 100644
index 00000000000..66cb58addc6
--- /dev/null
+++ b/chromium/weblayer/browser/persistence/browser_persister_file_utils.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
+#define WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace weblayer {
+
+class ProfileImpl;
+
+// Returns the set of known persistence ids for the profile at |path|.
+base::flat_set<std::string> GetBrowserPersistenceIdsOnBackgroundThread(
+ const base::FilePath& path);
+
+// Returns the path to save persistence information. |base_path| is the base
+// path of the profile, and |browser_id| the persistence id.
+base::FilePath BuildPathForBrowserPersister(const base::FilePath& base_path,
+ const std::string& browser_id);
+
+// Implementation of RemoveBrowserPersistenceStorage(). Tries to remove all
+// the persistence files for the set of browser persistence ids.
+void RemoveBrowserPersistenceStorageImpl(
+ ProfileImpl* profile,
+ base::OnceCallback<void(bool)> done_callback,
+ base::flat_set<std::string> ids);
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_PERSISTENCE_BROWSER_PERSISTER_FILE_UTILS_H_
diff --git a/chromium/weblayer/browser/persistence/minimal_browser_persister_browsertest.cc b/chromium/weblayer/browser/persistence/minimal_browser_persister_browsertest.cc
index 093fb4cd4f7..2d9ab733610 100644
--- a/chromium/weblayer/browser/persistence/minimal_browser_persister_browsertest.cc
+++ b/chromium/weblayer/browser/persistence/minimal_browser_persister_browsertest.cc
@@ -31,7 +31,7 @@ class MinimalBrowserPersisterTest : public WebLayerBrowserTest {
WebLayerBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
browser_ = Browser::Create(GetProfile(), nullptr);
- tab_ = static_cast<TabImpl*>(browser_->AddTab(Tab::Create(GetProfile())));
+ tab_ = static_cast<TabImpl*>(browser_->CreateTab());
browser_->SetActiveTab(tab_);
}
void PostRunTestOnMainThread() override {
@@ -83,7 +83,7 @@ IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, SingleTab) {
IN_PROC_BROWSER_TEST_F(MinimalBrowserPersisterTest, TwoTabs) {
NavigateAndWaitForCompletion(url1(), tab_);
- Tab* tab2 = browser_->AddTab(Tab::Create(GetProfile()));
+ Tab* tab2 = browser_->CreateTab();
NavigateAndWaitForCompletion(url2(), tab2);
browser_->SetActiveTab(tab2);
diff --git a/chromium/weblayer/browser/popup_blocker_browsertest.cc b/chromium/weblayer/browser/popup_blocker_browsertest.cc
new file mode 100644
index 00000000000..bd53f10dda5
--- /dev/null
+++ b/chromium/weblayer/browser/popup_blocker_browsertest.cc
@@ -0,0 +1,265 @@
+// 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.
+
+#include "build/build_config.h"
+#include "components/blocked_content/popup_blocker_tab_helper.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/host_content_settings_map_factory.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/browser/tab_impl.h"
+#include "weblayer/public/browser.h"
+#include "weblayer/public/browser_observer.h"
+#include "weblayer/public/navigation_controller.h"
+#include "weblayer/public/new_tab_delegate.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/test_navigation_observer.h"
+#include "weblayer/test/weblayer_browser_test.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+
+class PopupBlockerBrowserTest : public WebLayerBrowserTest,
+ public NewTabDelegate,
+ public BrowserObserver {
+ public:
+ // WebLayerBrowserTest:
+ void SetUpOnMainThread() override {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ original_tab_ = shell()->tab();
+#if !defined(OS_ANDROID)
+ // Android does this in Java.
+ original_tab_->SetNewTabDelegate(this);
+#endif
+ shell()->browser()->AddObserver(this);
+
+ NavigateAndWaitForCompletion(embedded_test_server()->GetURL("/echo"),
+ original_tab_);
+ }
+ void TearDownOnMainThread() override {
+ shell()->browser()->RemoveObserver(this);
+ }
+
+ // NewTabDelegate:
+ void OnNewTab(Tab* new_tab, NewTabType type) override {}
+ void CloseTab() override {}
+
+ // BrowserObserver:
+ void OnTabAdded(Tab* tab) override {
+ new_tab_ = tab;
+ if (new_tab_run_loop_)
+ new_tab_run_loop_->Quit();
+ }
+ void OnTabRemoved(Tab* tab, bool active_tab_changed) override {
+ ASSERT_EQ(tab, new_tab_);
+ new_tab_ = nullptr;
+ if (close_tab_run_loop_)
+ close_tab_run_loop_->Quit();
+ }
+
+ size_t GetBlockedPopupCount() {
+ return blocked_content::PopupBlockerTabHelper::FromWebContents(
+ GetWebContents(original_tab_))
+ ->GetBlockedPopupsCount();
+ }
+
+ content::WebContents* GetWebContents(Tab* tab) {
+ return static_cast<TabImpl*>(tab)->web_contents();
+ }
+
+ Tab* WaitForNewTab() {
+ if (!new_tab_) {
+ new_tab_run_loop_ = std::make_unique<base::RunLoop>();
+ new_tab_run_loop_->Run();
+ new_tab_run_loop_ = nullptr;
+ }
+ return new_tab_;
+ }
+
+ void WaitForCloseTab() {
+ if (new_tab_) {
+ close_tab_run_loop_ = std::make_unique<base::RunLoop>();
+ close_tab_run_loop_->Run();
+ close_tab_run_loop_ = nullptr;
+ }
+ ASSERT_FALSE(new_tab_);
+ }
+
+ void ExpectTabURL(Tab* tab, const GURL& url) {
+ if (tab->GetNavigationController()->GetNavigationListSize() > 0) {
+ EXPECT_EQ(tab->GetNavigationController()->GetNavigationEntryDisplayURL(0),
+ url);
+ } else {
+ TestNavigationObserver(
+ url, TestNavigationObserver::NavigationEvent::kCompletion, tab)
+ .Wait();
+ }
+ }
+
+ Tab* ShowPopup(const GURL& url) {
+ auto* popup_blocker =
+ blocked_content::PopupBlockerTabHelper::FromWebContents(
+ GetWebContents(original_tab_));
+ popup_blocker->ShowBlockedPopup(
+ popup_blocker->GetBlockedPopupRequests().begin()->first,
+ WindowOpenDisposition::NEW_FOREGROUND_TAB);
+ Tab* new_tab = WaitForNewTab();
+ ExpectTabURL(new_tab, url);
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+ return new_tab;
+ }
+
+ Tab* original_tab() { return original_tab_; }
+
+ private:
+ std::unique_ptr<base::RunLoop> new_tab_run_loop_;
+ std::unique_ptr<base::RunLoop> close_tab_run_loop_;
+
+ Tab* original_tab_ = nullptr;
+ Tab* new_tab_ = nullptr;
+};
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, BlocksPopup) {
+ ExecuteScript(original_tab(), "window.open('https://google.com')", true);
+ EXPECT_EQ(GetBlockedPopupCount(), 1u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, BlocksMultiplePopups) {
+ ExecuteScript(original_tab(), "window.open('https://google.com')", true);
+ ExecuteScript(original_tab(), "window.open('https://google.com')", true);
+ EXPECT_EQ(GetBlockedPopupCount(), 2u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, DoesNotBlockUserGesture) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ ExecuteScriptWithUserGesture(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()));
+
+ Tab* new_tab = WaitForNewTab();
+ ExpectTabURL(new_tab, popup_url);
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, OpensBlockedPopup) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ ExecuteScript(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()), true);
+ EXPECT_EQ(GetBlockedPopupCount(), 1u);
+
+ Tab* new_tab = ShowPopup(popup_url);
+
+ // Blocked popups should no longer have the opener set to match Chrome
+ // behavior.
+ EXPECT_FALSE(GetWebContents(new_tab)->HasOpener());
+ // Make sure we can cleanly close the popup, and there's no crash.
+ ExecuteScriptWithUserGesture(new_tab, "window.close()");
+ WaitForCloseTab();
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ AllowsPopupThroughContentSettingException) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ GetWebContents(original_tab())->GetBrowserContext())
+ ->SetContentSettingDefaultScope(popup_url, GURL(),
+ ContentSettingsType::POPUPS,
+ std::string(), CONTENT_SETTING_ALLOW);
+ ExecuteScript(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()), true);
+ Tab* new_tab = WaitForNewTab();
+ ExpectTabURL(new_tab, popup_url);
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ AllowsPopupThroughContentSettingDefaultValue) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ GetWebContents(original_tab())->GetBrowserContext())
+ ->SetDefaultContentSetting(ContentSettingsType::POPUPS,
+ CONTENT_SETTING_ALLOW);
+ ExecuteScript(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()), true);
+ Tab* new_tab = WaitForNewTab();
+ ExpectTabURL(new_tab, popup_url);
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ BlockPopupThroughContentSettingException) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ GetWebContents(original_tab())->GetBrowserContext())
+ ->SetDefaultContentSetting(ContentSettingsType::POPUPS,
+ CONTENT_SETTING_ALLOW);
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ GetWebContents(original_tab())->GetBrowserContext())
+ ->SetContentSettingDefaultScope(popup_url, GURL(),
+ ContentSettingsType::POPUPS,
+ std::string(), CONTENT_SETTING_BLOCK);
+ ExecuteScript(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()), true);
+ EXPECT_EQ(GetBlockedPopupCount(), 1u);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ BlocksAndOpensPopupFromOpenURL) {
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ content::OpenURLParams params(popup_url, content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, true);
+ params.initiator_origin = url::Origin::Create(popup_url);
+ GetWebContents(original_tab())->OpenURL(params);
+ EXPECT_EQ(GetBlockedPopupCount(), 1u);
+
+ ShowPopup(popup_url);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ DoesNotOpenPopupWithoutNewTabDelegate) {
+ NewTabDelegate* old_delegate =
+ static_cast<TabImpl*>(original_tab())->new_tab_delegate();
+ original_tab()->SetNewTabDelegate(nullptr);
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ ExecuteScriptWithUserGesture(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()));
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+
+ // Navigate the original tab, then make sure we still only have a single tab.
+ NavigateAndWaitForCompletion(embedded_test_server()->GetURL("/echo"),
+ original_tab());
+ EXPECT_EQ(shell()->browser()->GetTabs().size(), 1u);
+
+ // Restore the old delegate to make sure it is cleaned up on Android.
+ original_tab()->SetNewTabDelegate(old_delegate);
+}
+
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
+ DoesNotOpenBlockedPopupWithoutNewTabDelegate) {
+ NewTabDelegate* old_delegate =
+ static_cast<TabImpl*>(original_tab())->new_tab_delegate();
+ original_tab()->SetNewTabDelegate(nullptr);
+ GURL popup_url = embedded_test_server()->GetURL("/echo?popup");
+ ExecuteScript(
+ original_tab(),
+ base::StringPrintf("window.open('%s')", popup_url.spec().c_str()), true);
+ EXPECT_EQ(GetBlockedPopupCount(), 0u);
+
+ // Navigate the original tab, then make sure we still only have a single tab.
+ NavigateAndWaitForCompletion(embedded_test_server()->GetURL("/echo"),
+ original_tab());
+ EXPECT_EQ(shell()->browser()->GetTabs().size(), 1u);
+
+ // Restore the old delegate to make sure it is cleaned up on Android.
+ original_tab()->SetNewTabDelegate(old_delegate);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/popup_navigation_delegate_impl.cc b/chromium/weblayer/browser/popup_navigation_delegate_impl.cc
new file mode 100644
index 00000000000..01a917647cd
--- /dev/null
+++ b/chromium/weblayer/browser/popup_navigation_delegate_impl.cc
@@ -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.
+
+#include "weblayer/browser/popup_navigation_delegate_impl.h"
+
+#include "build/build_config.h"
+#include "content/public/browser/web_contents.h"
+#include "weblayer/browser/host_content_settings_map_factory.h"
+#include "weblayer/browser/infobar_service.h"
+
+#if defined(OS_ANDROID)
+#include "components/blocked_content/android/popup_blocked_infobar_delegate.h"
+#endif
+
+namespace weblayer {
+
+PopupNavigationDelegateImpl::PopupNavigationDelegateImpl(
+ const content::OpenURLParams& params,
+ content::WebContents* source_contents,
+ content::RenderFrameHost* opener)
+ : params_(params),
+ source_contents_(source_contents),
+ opener_(opener),
+ original_user_gesture_(params_.user_gesture) {}
+
+content::RenderFrameHost* PopupNavigationDelegateImpl::GetOpener() {
+ return opener_;
+}
+
+bool PopupNavigationDelegateImpl::GetOriginalUserGesture() {
+ return original_user_gesture_;
+}
+
+const GURL& PopupNavigationDelegateImpl::GetURL() {
+ return params_.url;
+}
+
+blocked_content::PopupNavigationDelegate::NavigateResult
+PopupNavigationDelegateImpl::NavigateWithGesture(
+ const blink::mojom::WindowFeatures& window_features,
+ base::Optional<WindowOpenDisposition> updated_disposition) {
+ // It's safe to mutate |params_| here because NavigateWithGesture() will only
+ // be called once, and the user gesture value has already been saved in
+ // |original_user_gesture_|.
+ params_.user_gesture = true;
+ if (updated_disposition)
+ params_.disposition = updated_disposition.value();
+ content::WebContents* new_contents = source_contents_->OpenURL(params_);
+ return NavigateResult{
+ new_contents,
+ params_.disposition,
+ };
+}
+
+void PopupNavigationDelegateImpl::OnPopupBlocked(
+ content::WebContents* web_contents,
+ int total_popups_blocked_on_page) {
+#if defined(OS_ANDROID)
+ blocked_content::PopupBlockedInfoBarDelegate::Create(
+ InfoBarService::FromWebContents(web_contents),
+ total_popups_blocked_on_page,
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ web_contents->GetBrowserContext()),
+ base::NullCallback());
+#endif
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/popup_navigation_delegate_impl.h b/chromium/weblayer/browser/popup_navigation_delegate_impl.h
new file mode 100644
index 00000000000..7e049647513
--- /dev/null
+++ b/chromium/weblayer/browser/popup_navigation_delegate_impl.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_POPUP_NAVIGATION_DELEGATE_IMPL_H_
+#define WEBLAYER_BROWSER_POPUP_NAVIGATION_DELEGATE_IMPL_H_
+
+#include "components/blocked_content/popup_navigation_delegate.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace weblayer {
+
+class PopupNavigationDelegateImpl
+ : public blocked_content::PopupNavigationDelegate {
+ public:
+ PopupNavigationDelegateImpl(const content::OpenURLParams& params,
+ content::WebContents* source_contents,
+ content::RenderFrameHost* opener);
+
+ // blocked_content::PopupNavigationDelegate:
+ content::RenderFrameHost* GetOpener() override;
+ bool GetOriginalUserGesture() override;
+ const GURL& GetURL() override;
+ NavigateResult NavigateWithGesture(
+ const blink::mojom::WindowFeatures& window_features,
+ base::Optional<WindowOpenDisposition> updated_disposition) override;
+ void OnPopupBlocked(content::WebContents* web_contents,
+ int total_popups_blocked_on_page) override;
+
+ const content::OpenURLParams& params() const { return params_; }
+
+ private:
+ content::OpenURLParams params_;
+ content::WebContents* source_contents_;
+ content::RenderFrameHost* opener_;
+ const bool original_user_gesture_;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_POPUP_NAVIGATION_DELEGATE_IMPL_H_
diff --git a/chromium/weblayer/browser/prefetch_browsertest.cc b/chromium/weblayer/browser/prefetch_browsertest.cc
new file mode 100644
index 00000000000..796d8905d73
--- /dev/null
+++ b/chromium/weblayer/browser/prefetch_browsertest.cc
@@ -0,0 +1,125 @@
+// 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.
+
+#include "base/files/file_path.h"
+#include "base/test/bind_test_util.h"
+#include "components/network_session_configurator/common/network_switches.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "weblayer/browser/tab_impl.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/weblayer_browser_test.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+namespace {
+const char kPrefetchPage[] = "/simple_prefetch.html";
+const char kRedirectPrefetchPage[] = "/redirect_prefetch.html";
+const char kRedirectPrefetchUrl[] = "/redirect";
+const char kRedirectedPrefetchUrl[] = "/redirected";
+const char kPrefetchTarget[] = "/prefetch_target.lnk";
+} // namespace
+
+class PrefetchBrowserTest : public WebLayerBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ // The test makes requests to google.com which we want to redirect to the
+ // test server.
+ host_resolver()->AddRule("*", "127.0.0.1");
+
+ embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
+ &PrefetchBrowserTest::MonitorRequest, base::Unretained(this)));
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ // Set a dummy variation ID to send X-Client-Data header to Google hosts
+ // in RedirectedPrefetch test.
+ command_line->AppendSwitchASCII("force-variation-ids", "42");
+ // Need to ignore cert errors to use a HTTPS server for the test domains.
+ command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+ }
+
+ bool RunPrefetchExperiment(GURL url, const base::string16 expected_title) {
+ content::TitleWatcher title_watcher(
+ static_cast<TabImpl*>(shell()->tab())->web_contents(), expected_title);
+ NavigateAndWaitForCompletion(url, shell());
+ return expected_title == title_watcher.WaitAndGetTitle();
+ }
+
+ protected:
+ bool prefetch_target_request_seen_ = false;
+
+ private:
+ void MonitorRequest(const net::test_server::HttpRequest& request) {
+ if (request.relative_url == std::string(kPrefetchTarget)) {
+ prefetch_target_request_seen_ = true;
+ }
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTest, PrefetchWorks) {
+ // Set real NetworkChangeNotifier singleton aside.
+ std::unique_ptr<net::NetworkChangeNotifier::DisableForTest> disable_for_test(
+ new net::NetworkChangeNotifier::DisableForTest);
+ ASSERT_FALSE(prefetch_target_request_seen_);
+ EXPECT_TRUE(
+ RunPrefetchExperiment(embedded_test_server()->GetURL(kPrefetchPage),
+ base::ASCIIToUTF16("link onload")));
+ EXPECT_TRUE(prefetch_target_request_seen_);
+}
+
+// https://crbug.com/922362: When the prefetched request is redirected, DCHECKs
+// in PrefetchURLLoader::FollowRedirect() failed due to "X-Client-Data" in
+// removed_headers. Verify that it no longer does.
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTest, RedirectedPrefetch) {
+ std::vector<net::test_server::HttpRequest> requests;
+ net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+ https_server.RegisterRequestHandler(base::BindLambdaForTesting(
+ [&requests](const net::test_server::HttpRequest& request)
+ -> std::unique_ptr<net::test_server::HttpResponse> {
+ auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+ if (request.relative_url == std::string(kRedirectPrefetchPage)) {
+ requests.push_back(request);
+ response->set_content_type("text/html");
+ response->set_content(
+ base::StringPrintf("<link rel=\"prefetch\" href=\"%s\" "
+ "onload=\"document.title='done'\">",
+ kRedirectPrefetchUrl));
+ return response;
+ } else if (request.relative_url == std::string(kRedirectPrefetchUrl)) {
+ requests.push_back(request);
+ response->set_code(net::HTTP_MOVED_PERMANENTLY);
+ response->AddCustomHeader(
+ "Location", base::StringPrintf("https://example.com:%s%s",
+ request.GetURL().port().c_str(),
+ kRedirectedPrefetchUrl));
+ return response;
+ } else if (request.relative_url ==
+ std::string(kRedirectedPrefetchUrl)) {
+ requests.push_back(request);
+ return response;
+ }
+ return nullptr;
+ }));
+
+ https_server.ServeFilesFromSourceDirectory(
+ base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+ ASSERT_TRUE(https_server.Start());
+
+ GURL url = https_server.GetURL("www.google.com", kRedirectPrefetchPage);
+ EXPECT_TRUE(RunPrefetchExperiment(url, base::ASCIIToUTF16("done")));
+ ASSERT_EQ(3U, requests.size());
+
+ EXPECT_EQ(base::StringPrintf("www.google.com:%u", https_server.port()),
+ requests[0].headers["Host"]);
+ EXPECT_EQ(kRedirectPrefetchPage, requests[0].relative_url);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/profile_browsertest.cc b/chromium/weblayer/browser/profile_browsertest.cc
new file mode 100644
index 00000000000..3b8bbf3983b
--- /dev/null
+++ b/chromium/weblayer/browser/profile_browsertest.cc
@@ -0,0 +1,27 @@
+// 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.
+
+#include "build/build_config.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/test/weblayer_browser_test.h"
+
+namespace weblayer {
+
+using ProfileBrowsertest = WebLayerBrowserTest;
+
+// TODO(crbug.com/654704): Android does not support PRE_ tests.
+#if !defined(OS_ANDROID)
+
+// UKM enabling via Profile persists across restarts.
+IN_PROC_BROWSER_TEST_F(ProfileBrowsertest, PRE_PersistUKM) {
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileBrowsertest, PersistUKM) {
+ ASSERT_TRUE(GetProfile()->GetBooleanSetting(SettingType::UKM_ENABLED));
+}
+
+#endif // !defined(OS_ANDROID)
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/profile_disk_operations.cc b/chromium/weblayer/browser/profile_disk_operations.cc
index 3b1a58d3f21..736c102a8a3 100644
--- a/chromium/weblayer/browser/profile_disk_operations.cc
+++ b/chromium/weblayer/browser/profile_disk_operations.cc
@@ -6,6 +6,7 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
+#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
diff --git a/chromium/weblayer/browser/profile_disk_operations_unittests.cc b/chromium/weblayer/browser/profile_disk_operations_unittests.cc
index dd1f381be42..801587d463b 100644
--- a/chromium/weblayer/browser/profile_disk_operations_unittests.cc
+++ b/chromium/weblayer/browser/profile_disk_operations_unittests.cc
@@ -6,11 +6,11 @@
#include <string>
#include <vector>
+#include "base/base_paths.h"
#include "base/check.h"
#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_path_override.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "weblayer/browser/profile_disk_operations.h"
@@ -19,20 +19,10 @@
namespace weblayer {
class ProfileDiskOperationsTest : public testing::Test {
- public:
- void SetUp() override {
- CHECK(data_temp_dir_.CreateUniqueTempDir());
- base::PathService::Override(DIR_USER_DATA, data_temp_dir_.GetPath());
-#if defined(OS_POSIX)
- CHECK(cache_temp_dir_.CreateUniqueTempDir());
- base::PathService::Override(base::DIR_CACHE, cache_temp_dir_.GetPath());
-#endif
- }
-
protected:
- base::ScopedTempDir data_temp_dir_;
+ base::ScopedPathOverride data_dir_override_{DIR_USER_DATA};
#if defined(OS_POSIX)
- base::ScopedTempDir cache_temp_dir_;
+ base::ScopedPathOverride cache_dir_override_{base::DIR_CACHE};
#endif
};
diff --git a/chromium/weblayer/browser/profile_impl.cc b/chromium/weblayer/browser/profile_impl.cc
index 8e1c744f106..3cc8cc635af 100644
--- a/chromium/weblayer/browser/profile_impl.cc
+++ b/chromium/weblayer/browser/profile_impl.cc
@@ -4,6 +4,7 @@
#include "weblayer/browser/profile_impl.h"
+#include <algorithm>
#include <memory>
#include <string>
#include <utility>
@@ -11,7 +12,8 @@
#include "base/bind.h"
#include "base/callback_forward.h"
-#include "base/task/post_task.h"
+#include "base/no_destructor.h"
+#include "base/observer_list.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
@@ -23,10 +25,16 @@
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/download_manager.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "services/network/public/mojom/network_context.mojom.h"
+#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/browser_list.h"
+#include "weblayer/browser/browsing_data_remover_delegate.h"
#include "weblayer/browser/cookie_manager_impl.h"
+#include "weblayer/browser/persistence/browser_persister_file_utils.h"
#include "weblayer/browser/tab_impl.h"
#if defined(OS_ANDROID)
@@ -34,6 +42,8 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/unified_consent/pref_names.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/java/jni/ProfileImpl_jni.h"
#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
@@ -63,6 +73,18 @@ base::SequencedTaskRunner* GetBackgroundDiskOperationTaskRunner() {
return task_runner.get()->get();
}
+std::set<ProfileImpl*>& GetProfiles() {
+ static base::NoDestructor<std::set<ProfileImpl*>> s_all_profiles;
+ return *s_all_profiles;
+}
+
+base::ObserverList<ProfileImpl::ProfileObserver>::Unchecked& GetObservers() {
+ static base::NoDestructor<
+ base::ObserverList<ProfileImpl::ProfileObserver>::Unchecked>
+ s_observers;
+ return *s_observers;
+}
+
#if defined(OS_ANDROID)
void PassFilePathsToJavaCallback(
const base::android::ScopedJavaGlobalRef<jobject>& callback,
@@ -71,6 +93,24 @@ void PassFilePathsToJavaCallback(
callback, base::android::ToJavaArrayOfStrings(
base::android::AttachCurrentThread(), file_paths));
}
+
+void OnGotBrowserPersistenceIds(
+ const base::android::ScopedJavaGlobalRef<jobject>& callback,
+ base::flat_set<std::string> ids) {
+ std::vector<std::string> as_vector;
+ for (const std::string& id : ids)
+ as_vector.push_back(id);
+ base::android::RunObjectCallbackAndroid(
+ callback,
+ base::android::ToJavaArrayOfStrings(AttachCurrentThread(), as_vector));
+}
+
+void OnDidRemoveBrowserPersistenceStorage(
+ const base::android::ScopedJavaGlobalRef<jobject>& callback,
+ bool result) {
+ base::android::RunBooleanCallbackAndroid(callback, result);
+}
+
#endif // OS_ANDROID
} // namespace
@@ -87,8 +127,8 @@ class ProfileImpl::DataClearer : public content::BrowsingDataRemover::Observer {
~DataClearer() override { remover_->RemoveObserver(this); }
- void ClearData(int mask, base::Time from_time, base::Time to_time) {
- int origin_types =
+ void ClearData(uint64_t mask, base::Time from_time, base::Time to_time) {
+ uint64_t origin_types =
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
remover_->RemoveAndReply(from_time, to_time, mask, origin_types, this);
@@ -118,6 +158,10 @@ ProfileImpl::ProfileImpl(const std::string& name)
info_ = CreateProfileInfo(name);
}
+ GetProfiles().insert(this);
+ for (auto& observer : GetObservers())
+ observer.ProfileCreated(this);
+
if (!g_first_profile_created) {
g_first_profile_created = true;
GetBackgroundDiskOperationTaskRunner()->PostTask(
@@ -127,12 +171,18 @@ ProfileImpl::ProfileImpl(const std::string& name)
// Ensure WebCacheManager is created so that it starts observing
// OnRenderProcessHostCreated events.
web_cache::WebCacheManager::GetInstance();
+
+#if defined(OS_ANDROID)
+ WebLayerMetricsServiceClient::GetInstance()->UpdateUkm(false);
+#endif
}
ProfileImpl::~ProfileImpl() {
- DCHECK_EQ(num_browser_impl_, 0u);
if (browser_context_)
browser_context_->ShutdownStoragePartitions();
+ GetProfiles().erase(this);
+ for (auto& observer : GetObservers())
+ observer.ProfileDestroyed(this);
}
ProfileImpl* ProfileImpl::FromBrowserContext(
@@ -140,7 +190,19 @@ ProfileImpl* ProfileImpl::FromBrowserContext(
return static_cast<BrowserContextImpl*>(browser_context)->profile_impl();
}
-content::BrowserContext* ProfileImpl::GetBrowserContext() {
+std::set<ProfileImpl*> ProfileImpl::GetAllProfiles() {
+ return GetProfiles();
+}
+
+void ProfileImpl::AddProfileObserver(ProfileObserver* observer) {
+ GetObservers().AddObserver(observer);
+}
+
+void ProfileImpl::RemoveProfileObserver(ProfileObserver* observer) {
+ GetObservers().RemoveObserver(observer);
+}
+
+BrowserContextImpl* ProfileImpl::GetBrowserContext() {
if (browser_context_)
return browser_context_.get();
@@ -170,7 +232,7 @@ void ProfileImpl::ClearBrowsingData(
// browser_context_ and then BrowsingDataRemover, which in turn would call
// OnBrowsingDataRemoverDone(), even though the clearing hasn't been finished.
- int remove_mask = 0;
+ uint64_t remove_mask = 0;
// This follows what Chrome does: see browsing_data_bridge.cc.
for (auto data_type : data_types) {
switch (data_type) {
@@ -178,6 +240,7 @@ void ProfileImpl::ClearBrowsingData(
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_COOKIES;
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOM_STORAGE;
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES;
+ remove_mask |= BrowsingDataRemoverDelegate::DATA_TYPE_ISOLATED_ORIGINS;
break;
case BrowsingDataType::CACHE:
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE;
@@ -204,13 +267,33 @@ CookieManager* ProfileImpl::GetCookieManager() {
return cookie_manager_.get();
}
+void ProfileImpl::GetBrowserPersistenceIds(
+ base::OnceCallback<void(base::flat_set<std::string>)> callback) {
+ DCHECK(!browser_context_->IsOffTheRecord());
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::BindOnce(&GetBrowserPersistenceIdsOnBackgroundThread,
+ GetBrowserPersisterDataBaseDir()),
+ std::move(callback));
+}
+
+void ProfileImpl::RemoveBrowserPersistenceStorage(
+ base::OnceCallback<void(bool)> done_callback,
+ base::flat_set<std::string> ids) {
+ DCHECK(!browser_context_->IsOffTheRecord());
+ RemoveBrowserPersistenceStorageImpl(this, std::move(done_callback),
+ std::move(ids));
+}
+
// static
void ProfileImpl::NukeDataAfterRemovingData(
std::unique_ptr<ProfileImpl> profile,
base::OnceClosure done_callback) {
// Need PostTask to avoid reentrancy for deleting |browser_context_|.
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&ProfileImpl::DoNukeData, std::move(profile),
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ProfileImpl::DoNukeData, std::move(profile),
std::move(done_callback)));
}
@@ -268,7 +351,7 @@ std::unique_ptr<Profile> Profile::DestroyAndDeleteDataFromDisk(
std::unique_ptr<ProfileImpl> ProfileImpl::DestroyAndDeleteDataFromDisk(
std::unique_ptr<ProfileImpl> profile,
base::OnceClosure done_callback) {
- if (profile->num_browser_impl_ > 0)
+ if (profile->GetNumberOfBrowsers() > 0)
return profile;
GetBackgroundDiskOperationTaskRunner()->PostTaskAndReply(
@@ -282,9 +365,7 @@ std::unique_ptr<ProfileImpl> ProfileImpl::DestroyAndDeleteDataFromDisk(
void ProfileImpl::OnProfileMarked(std::unique_ptr<ProfileImpl> profile,
base::OnceClosure done_callback) {
// Try to finish all writes and remove all data before nuking the profile.
- static_cast<BrowserContextImpl*>(profile->GetBrowserContext())
- ->pref_service()
- ->CommitPendingWrite();
+ profile->GetBrowserContext()->pref_service()->CommitPendingWrite();
// Unretained is safe here because DataClearer is owned by
// BrowserContextImpl which is owned by this.
@@ -292,7 +373,7 @@ void ProfileImpl::OnProfileMarked(std::unique_ptr<ProfileImpl> profile,
profile->GetBrowserContext(),
base::BindOnce(&ProfileImpl::NukeDataAfterRemovingData,
std::move(profile), std::move(done_callback)));
- int remove_all_mask = 0x8fffffff;
+ uint64_t remove_all_mask = 0xffffffffffffffffull;
clearer->ClearData(remove_all_mask, base::Time::Min(), base::Time::Max());
}
@@ -329,7 +410,7 @@ static void JNI_ProfileImpl_EnumerateAllProfileNames(
}
jint ProfileImpl::GetNumBrowserImpl(JNIEnv* env) {
- return num_browser_impl_;
+ return GetNumberOfBrowsers();
}
jlong ProfileImpl::GetBrowserContext(JNIEnv* env) {
@@ -397,39 +478,107 @@ jboolean ProfileImpl::GetBooleanSetting(JNIEnv* env, jint j_type) {
return GetBooleanSetting(static_cast<SettingType>(j_type));
}
-#endif // OS_ANDROID
+void ProfileImpl::GetBrowserPersistenceIds(
+ JNIEnv* env,
+ const base::android::JavaRef<jobject>& j_callback) {
+ GetBrowserPersistenceIds(
+ base::BindOnce(&OnGotBrowserPersistenceIds,
+ base::android::ScopedJavaGlobalRef<jobject>(j_callback)));
+}
-void ProfileImpl::IncrementBrowserImplCount() {
- num_browser_impl_++;
+void ProfileImpl::RemoveBrowserPersistenceStorage(
+ JNIEnv* env,
+ const base::android::JavaRef<jobjectArray>& j_ids,
+ const base::android::JavaRef<jobject>& j_callback) {
+ std::vector<std::string> ids;
+ base::android::AppendJavaStringArrayToStringVector(env, j_ids, &ids);
+ RemoveBrowserPersistenceStorage(
+ base::BindOnce(&OnDidRemoveBrowserPersistenceStorage,
+ base::android::ScopedJavaGlobalRef<jobject>(j_callback)),
+ base::flat_set<std::string>(ids.begin(), ids.end()));
}
-void ProfileImpl::DecrementBrowserImplCount() {
- DCHECK_GT(num_browser_impl_, 0u);
- num_browser_impl_--;
+void ProfileImpl::PrepareForPossibleCrossOriginNavigation(JNIEnv* env) {
+ PrepareForPossibleCrossOriginNavigation();
}
+#endif // OS_ANDROID
+
base::FilePath ProfileImpl::GetBrowserPersisterDataBaseDir() const {
return ComputeBrowserPersisterDataBaseDir(info_);
}
void ProfileImpl::SetBooleanSetting(SettingType type, bool value) {
+ auto* pref_service = GetBrowserContext()->pref_service();
switch (type) {
case SettingType::BASIC_SAFE_BROWSING_ENABLED:
- basic_safe_browsing_enabled_ = value;
#if defined(OS_ANDROID)
- BrowserProcess::GetInstance()
- ->GetSafeBrowsingService(weblayer::GetUserAgent())
- ->SetSafeBrowsingDisabled(!basic_safe_browsing_enabled_);
+ pref_service->SetBoolean(::prefs::kSafeBrowsingEnabled, value);
+ pref_service->SetBoolean(::prefs::kSafeBrowsingEnhanced, false);
+#endif
+ break;
+ case SettingType::UKM_ENABLED: {
+#if defined(OS_ANDROID)
+ bool old_value = pref_service->GetBoolean(prefs::kUkmEnabled);
#endif
+ pref_service->SetBoolean(prefs::kUkmEnabled, value);
+#if defined(OS_ANDROID)
+ // Trigger a purge if the current state no longer allows UKM.
+ bool must_purge = old_value && !value;
+ WebLayerMetricsServiceClient::GetInstance()->UpdateUkm(must_purge);
+#endif
+ break;
+ }
+ case SettingType::EXTENDED_REPORTING_SAFE_BROWSING_ENABLED:
+#if defined(OS_ANDROID)
+ pref_service->SetBoolean(::prefs::kSafeBrowsingScoutReportingEnabled,
+ value);
+#endif
+ break;
+ case SettingType::REAL_TIME_SAFE_BROWSING_ENABLED:
+#if defined(OS_ANDROID)
+ pref_service->SetBoolean(
+ unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ value);
+#endif
+ break;
}
}
bool ProfileImpl::GetBooleanSetting(SettingType type) {
+ auto* pref_service = GetBrowserContext()->pref_service();
switch (type) {
case SettingType::BASIC_SAFE_BROWSING_ENABLED:
- return basic_safe_browsing_enabled_;
+#if defined(OS_ANDROID)
+ return safe_browsing::IsSafeBrowsingEnabled(*pref_service);
+#endif
+ return false;
+ case SettingType::UKM_ENABLED:
+ return pref_service->GetBoolean(prefs::kUkmEnabled);
+ case SettingType::EXTENDED_REPORTING_SAFE_BROWSING_ENABLED:
+#if defined(OS_ANDROID)
+ return pref_service->GetBoolean(
+ ::prefs::kSafeBrowsingScoutReportingEnabled);
+#endif
+ return false;
+ case SettingType::REAL_TIME_SAFE_BROWSING_ENABLED:
+#if defined(OS_ANDROID)
+ return pref_service->GetBoolean(
+ unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+#endif
+ return false;
}
NOTREACHED();
}
+void ProfileImpl::PrepareForPossibleCrossOriginNavigation() {
+ content::RenderProcessHost::WarmupSpareRenderProcessHost(GetBrowserContext());
+}
+
+int ProfileImpl::GetNumberOfBrowsers() {
+ const auto& browsers = BrowserList::GetInstance()->browsers();
+ return std::count_if(browsers.begin(), browsers.end(),
+ [this](BrowserImpl* b) { return b->profile() == this; });
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/profile_impl.h b/chromium/weblayer/browser/profile_impl.h
index fbd30a82c8a..d845d6bc646 100644
--- a/chromium/weblayer/browser/profile_impl.h
+++ b/chromium/weblayer/browser/profile_impl.h
@@ -5,6 +5,8 @@
#ifndef WEBLAYER_BROWSER_PROFILE_IMPL_H_
#define WEBLAYER_BROWSER_PROFILE_IMPL_H_
+#include <set>
+
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -46,7 +48,22 @@ class ProfileImpl : public Profile {
static ProfileImpl* FromBrowserContext(
content::BrowserContext* browser_context);
- content::BrowserContext* GetBrowserContext();
+ static std::set<ProfileImpl*> GetAllProfiles();
+
+ // Allows getting notified when profiles are created or destroyed.
+ class ProfileObserver {
+ public:
+ virtual void ProfileCreated(ProfileImpl* profile) {}
+ virtual void ProfileDestroyed(ProfileImpl* profile) {}
+
+ protected:
+ virtual ~ProfileObserver() = default;
+ };
+
+ static void AddProfileObserver(ProfileObserver* observer);
+ static void RemoveProfileObserver(ProfileObserver* observer);
+
+ BrowserContextImpl* GetBrowserContext();
// Called when the download subsystem has finished initializing. By this point
// information about downloads that were interrupted by a previous crash would
@@ -55,6 +72,7 @@ class ProfileImpl : public Profile {
// Path data is stored at, empty if off-the-record.
const base::FilePath& data_path() const { return info_.data_path; }
+ const std::string& name() const { return info_.name; }
DownloadDelegate* download_delegate() { return download_delegate_; }
// Profile implementation:
@@ -65,8 +83,14 @@ class ProfileImpl : public Profile {
void SetDownloadDirectory(const base::FilePath& directory) override;
void SetDownloadDelegate(DownloadDelegate* delegate) override;
CookieManager* GetCookieManager() override;
+ void GetBrowserPersistenceIds(
+ base::OnceCallback<void(base::flat_set<std::string>)> callback) override;
+ void RemoveBrowserPersistenceStorage(
+ base::OnceCallback<void(bool)> done_callback,
+ base::flat_set<std::string> ids) override;
void SetBooleanSetting(SettingType type, bool value) override;
bool GetBooleanSetting(SettingType type) override;
+ void PrepareForPossibleCrossOriginNavigation() override;
#if defined(OS_ANDROID)
ProfileImpl(JNIEnv* env,
@@ -91,10 +115,16 @@ class ProfileImpl : public Profile {
void EnsureBrowserContextInitialized(JNIEnv* env);
void SetBooleanSetting(JNIEnv* env, jint j_type, jboolean j_value);
jboolean GetBooleanSetting(JNIEnv* env, jint j_type);
+ void GetBrowserPersistenceIds(
+ JNIEnv* env,
+ const base::android::JavaRef<jobject>& j_callback);
+ void RemoveBrowserPersistenceStorage(
+ JNIEnv* env,
+ const base::android::JavaRef<jobjectArray>& j_ids,
+ const base::android::JavaRef<jobject>& j_callback);
+ void PrepareForPossibleCrossOriginNavigation(JNIEnv* env);
#endif
- void IncrementBrowserImplCount();
- void DecrementBrowserImplCount();
const base::FilePath& download_directory() { return download_directory_; }
// Get the directory where BrowserPersister stores tab state data. This will
@@ -115,6 +145,9 @@ class ProfileImpl : public Profile {
// Callback when the system locale has been updated.
void OnLocaleChanged();
+ // Returns the number of Browsers with this profile.
+ int GetNumberOfBrowsers();
+
ProfileInfo info_;
std::unique_ptr<BrowserContextImpl> browser_context_;
@@ -127,10 +160,6 @@ class ProfileImpl : public Profile {
std::unique_ptr<CookieManagerImpl> cookie_manager_;
- size_t num_browser_impl_ = 0u;
-
- bool basic_safe_browsing_enabled_ = true;
-
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> java_profile_;
#endif
diff --git a/chromium/weblayer/browser/safe_browsing/BUILD.gn b/chromium/weblayer/browser/safe_browsing/BUILD.gn
deleted file mode 100644
index 001f036d3e8..00000000000
--- a/chromium/weblayer/browser/safe_browsing/BUILD.gn
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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.
-
-import("//build/config/android/config.gni")
-
-assert(is_android)
-
-source_set("safe_browsing") {
- sources = [
- "safe_browsing_blocking_page.cc",
- "safe_browsing_blocking_page.h",
- "safe_browsing_navigation_throttle.cc",
- "safe_browsing_navigation_throttle.h",
- "safe_browsing_service.cc",
- "safe_browsing_service.h",
- "safe_browsing_subresource_helper.cc",
- "safe_browsing_subresource_helper.h",
- "safe_browsing_ui_manager.cc",
- "safe_browsing_ui_manager.h",
- "url_checker_delegate_impl.cc",
- "url_checker_delegate_impl.h",
- ]
- deps = [
- "//components/safe_browsing/android:remote_database_manager",
- "//components/safe_browsing/android:safe_browsing_api_handler",
- "//components/safe_browsing/content",
- "//components/safe_browsing/content/browser",
- "//components/safe_browsing/content/renderer:throttles",
- "//components/safe_browsing/core/browser",
- "//components/safe_browsing/core/browser:network_context",
- "//components/safe_browsing/core/common",
- "//components/safe_browsing/core/db:database_manager",
- "//components/security_interstitials/content:security_interstitial_page",
- "//components/security_interstitials/core:unsafe_resource",
- "//components/security_interstitials/core/",
- "//content/public/browser",
- "//skia",
- "//third_party/blink/public/common",
- ]
-}
diff --git a/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc
new file mode 100644
index 00000000000..d3bee53a080
--- /dev/null
+++ b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc
@@ -0,0 +1,60 @@
+// 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.
+
+#include "weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h"
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "content/public/browser/browser_context.h"
+#include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/feature_list_creator.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
+#include "weblayer/browser/user_agent.h"
+#include "weblayer/browser/verdict_cache_manager_factory.h"
+
+namespace weblayer {
+
+// static
+safe_browsing::RealTimeUrlLookupService*
+RealTimeUrlLookupServiceFactory::GetForBrowserContext(
+ content::BrowserContext* browser_context) {
+ return static_cast<safe_browsing::RealTimeUrlLookupService*>(
+ GetInstance()->GetServiceForBrowserContext(browser_context,
+ /* create= */ true));
+}
+
+// static
+RealTimeUrlLookupServiceFactory*
+RealTimeUrlLookupServiceFactory::GetInstance() {
+ return base::Singleton<RealTimeUrlLookupServiceFactory>::get();
+}
+
+RealTimeUrlLookupServiceFactory::RealTimeUrlLookupServiceFactory()
+ : BrowserContextKeyedServiceFactory(
+ "RealTimeUrlLookupService",
+ BrowserContextDependencyManager::GetInstance()) {}
+
+KeyedService* RealTimeUrlLookupServiceFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ auto url_loader_factory =
+ std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>(
+ BrowserProcess::GetInstance()
+ ->GetSafeBrowsingService(weblayer::GetUserAgent())
+ ->GetURLLoaderFactory());
+
+ return new safe_browsing::RealTimeUrlLookupService(
+ network::SharedURLLoaderFactory::Create(std::move(url_loader_factory)),
+ VerdictCacheManagerFactory::GetForBrowserContext(context),
+ nullptr /* identity manager */, nullptr /* profile sync service */,
+ static_cast<BrowserContextImpl*>(context)->pref_service(),
+ safe_browsing::GetProfileManagementStatus(nullptr),
+ false /* is_under_advanced_protection */,
+ static_cast<BrowserContextImpl*>(context)->IsOffTheRecord(),
+ FeatureListCreator::GetInstance()->variations_service());
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h
new file mode 100644
index 00000000000..1d9e6780a26
--- /dev/null
+++ b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_SAFE_BROWSING_REAL_TIME_URL_LOOKUP_SERVICE_FACTORY_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_REAL_TIME_URL_LOOKUP_SERVICE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class KeyedService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace safe_browsing {
+class RealTimeUrlLookupService;
+} // namespace safe_browsing
+
+namespace weblayer {
+
+// Singleton that owns RealTimeUrlLookupService objects and associates them
+// them with BrowserContextImpl instances.
+class RealTimeUrlLookupServiceFactory
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ // Creates the service if it doesn't exist already for the given
+ // |browser_context|. If the service already exists, return its pointer.
+ static safe_browsing::RealTimeUrlLookupService* GetForBrowserContext(
+ content::BrowserContext* browser_context);
+
+ // Get the singleton instance.
+ static RealTimeUrlLookupServiceFactory* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<RealTimeUrlLookupServiceFactory>;
+
+ RealTimeUrlLookupServiceFactory();
+ ~RealTimeUrlLookupServiceFactory() override = default;
+ RealTimeUrlLookupServiceFactory(const RealTimeUrlLookupServiceFactory&) =
+ delete;
+ RealTimeUrlLookupServiceFactory& operator=(
+ const RealTimeUrlLookupServiceFactory&) = delete;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_SAFE_BROWSING_REAL_TIME_URL_LOOKUP_SERVICE_FACTORY_H_
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_blocking_page.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_blocking_page.cc
index 34cf6cb561d..fd6c603a40f 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_blocking_page.cc
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -6,7 +6,9 @@
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/content/unsafe_resource_util.h"
+#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
#include "content/public/browser/navigation_entry.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h"
namespace weblayer {
@@ -43,11 +45,23 @@ SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage(
GURL url =
(main_frame_url.is_empty() && entry) ? entry->GetURL() : main_frame_url;
+ BrowserContextImpl* browser_context =
+ static_cast<BrowserContextImpl*>(web_contents->GetBrowserContext());
+ security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions
+ display_options =
+ BaseBlockingPage::CreateDefaultDisplayOptions(unsafe_resources);
+ display_options.is_extended_reporting_opt_in_allowed =
+ safe_browsing::IsExtendedReportingOptInAllowed(
+ *(browser_context->pref_service()));
+ display_options.is_extended_reporting_enabled =
+ safe_browsing::IsExtendedReportingEnabled(
+ *(browser_context->pref_service()));
+
return new SafeBrowsingBlockingPage(
ui_manager, web_contents, url, unsafe_resources,
CreateControllerClient(web_contents, unsafe_resources, ui_manager,
- nullptr /*pref_service*/),
- BaseBlockingPage::CreateDefaultDisplayOptions(unsafe_resources));
+ browser_context->pref_service()),
+ display_options);
}
security_interstitials::SecurityInterstitialPage::TypeID
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
index 51bd76179c4..77975384186 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
@@ -4,21 +4,28 @@
#include <map>
-#include "base/task/post_task.h"
+#include "components/prefs/pref_service.h"
#include "components/safe_browsing/android/safe_browsing_api_handler.h"
#include "components/safe_browsing/content/base_blocking_page.h"
#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/safe_browsing/safe_browsing_blocking_page.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/navigation.h"
#include "weblayer/public/navigation_controller.h"
+#include "weblayer/public/profile.h"
#include "weblayer/public/tab.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/load_completion_observer.h"
@@ -34,8 +41,8 @@ void RunCallbackOnIOThread(
callback,
safe_browsing::SBThreatType threat_type,
const safe_browsing::ThreatMetadata& metadata) {
- base::PostTask(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(std::move(*callback), threat_type, metadata));
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*callback), threat_type, metadata));
}
} // namespace
@@ -79,14 +86,29 @@ class SafeBrowsingBrowserTest : public WebLayerBrowserTest {
SafeBrowsingBrowserTest() : fake_handler_(new FakeSafeBrowsingApiHandler()) {}
~SafeBrowsingBrowserTest() override = default;
- // WebLayerBrowserTest:
void SetUpOnMainThread() override {
+ InitializeOnMainThread();
+ // Safe Browsing is enabled by default
+ ASSERT_TRUE(GetSafeBrowsingEnabled());
+ }
+
+ void InitializeOnMainThread() {
NavigateAndWaitForCompletion(GURL("about:blank"), shell());
safe_browsing::SafeBrowsingApiHandler::SetInstance(fake_handler_.get());
ASSERT_TRUE(embedded_test_server()->Start());
url_ = embedded_test_server()->GetURL("/simple_page.html");
}
+ void SetSafeBrowsingEnabled(bool value) {
+ GetProfile()->SetBooleanSetting(SettingType::BASIC_SAFE_BROWSING_ENABLED,
+ value);
+ }
+
+ bool GetSafeBrowsingEnabled() {
+ return GetProfile()->GetBooleanSetting(
+ SettingType::BASIC_SAFE_BROWSING_ENABLED);
+ }
+
void NavigateWithThreatType(const safe_browsing::SBThreatType& threatType,
bool expect_interstitial) {
fake_handler_->AddRestriction(url_, threatType);
@@ -106,6 +128,16 @@ class SafeBrowsingBrowserTest : public WebLayerBrowserTest {
}
}
+ void NavigateWithSubResourceAndThreatType(
+ const safe_browsing::SBThreatType& threat_type,
+ bool expect_interstitial) {
+ GURL page_with_script_url =
+ embedded_test_server()->GetURL("/simple_page_with_script.html");
+ GURL script_url = embedded_test_server()->GetURL("/script.js");
+ fake_handler_->AddRestriction(script_url, threat_type);
+ Navigate(page_with_script_url, expect_interstitial);
+ }
+
protected:
content::WebContents* GetWebContents() {
Tab* tab = shell()->tab();
@@ -126,6 +158,19 @@ class SafeBrowsingBrowserTest : public WebLayerBrowserTest {
bool HasInterstitial() { return GetSecurityInterstitialPage() != nullptr; }
+ void KillRenderer() {
+ content::RenderProcessHost* child_process =
+ static_cast<TabImpl*>(shell()->tab())
+ ->web_contents()
+ ->GetMainFrame()
+ ->GetProcess();
+ content::RenderProcessHostWatcher crash_observer(
+ child_process,
+ content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ child_process->Shutdown(0);
+ crash_observer.Wait();
+ }
+
std::unique_ptr<FakeSafeBrowsingApiHandler> fake_handler_;
GURL url_;
@@ -133,6 +178,21 @@ class SafeBrowsingBrowserTest : public WebLayerBrowserTest {
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBrowserTest);
};
+class SafeBrowsingDisabledBrowserTest : public SafeBrowsingBrowserTest {
+ public:
+ SafeBrowsingDisabledBrowserTest() {}
+ ~SafeBrowsingDisabledBrowserTest() override = default;
+
+ void SetUpOnMainThread() override {
+ SetSafeBrowsingEnabled(false);
+ SafeBrowsingBrowserTest::InitializeOnMainThread();
+ ASSERT_FALSE(GetSafeBrowsingEnabled());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDisabledBrowserTest);
+};
+
IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
DoesNotShowInterstitial_NoRestriction) {
Navigate(url_, false);
@@ -160,12 +220,70 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, ShowsInterstitial_Billing) {
IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
ShowsInterstitial_Malware_Subresource) {
- GURL page_with_script_url =
- embedded_test_server()->GetURL("/simple_page_with_script.html");
- GURL script_url = embedded_test_server()->GetURL("/script.js");
- fake_handler_->AddRestriction(script_url,
- safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
- Navigate(page_with_script_url, true);
+ NavigateWithSubResourceAndThreatType(
+ safe_browsing::SB_THREAT_TYPE_URL_MALWARE, true);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
+ DoesNotShowInterstitial_Phishing_disableSB) {
+ // Test that the browser checks the safe browsing setting for new navigations.
+ SetSafeBrowsingEnabled(false);
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
+ DoesNotShowInterstitial_Malware_Subresource_disableSB) {
+ // Test that new renderer checks the safe browsing setting.
+ SetSafeBrowsingEnabled(false);
+ KillRenderer();
+ NavigateWithSubResourceAndThreatType(
+ safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, CheckSetsPrefs) {
+ // Check that changing safe browsing setting sets corresponding pref,
+ // which is persistent.
+ PrefService* prefs = GetProfile()->GetBrowserContext()->pref_service();
+ SetSafeBrowsingEnabled(true);
+ EXPECT_TRUE(prefs->GetBoolean(::prefs::kSafeBrowsingEnabled));
+ SetSafeBrowsingEnabled(false);
+ EXPECT_FALSE(prefs->GetBoolean(::prefs::kSafeBrowsingEnabled));
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_NoRestriction) {
+ Navigate(url_, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Safe) {
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_SAFE, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Malware) {
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Phishing) {
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Unwanted) {
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_UNWANTED, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Billing) {
+ NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_BILLING, false);
+}
+
+IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
+ DoesNotShowInterstitial_Malware_Subresource) {
+ NavigateWithSubResourceAndThreatType(
+ safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
}
-} // namespace weblayer
+} // namespace weblayer \ No newline at end of file
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc
index 915e9a26f89..fca167feed4 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc
@@ -6,19 +6,24 @@
#include "base/bind.h"
#include "base/path_service.h"
-#include "base/task/post_task.h"
+#include "components/prefs/pref_service.h"
#include "components/safe_browsing/android/remote_database_manager.h"
#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_context.h"
+#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/safe_browsing/safe_browsing_navigation_throttle.h"
#include "weblayer/browser/safe_browsing/url_checker_delegate_impl.h"
@@ -30,12 +35,15 @@ network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams(
const std::string& user_agent) {
network::mojom::NetworkContextParamsPtr network_context_params =
network::mojom::NetworkContextParams::New();
+ network_context_params->cert_verifier_params = content::GetCertVerifierParams(
+ network::mojom::CertVerifierCreationParams::New());
network_context_params->user_agent = user_agent;
return network_context_params;
}
-// Helper method that checks the RenderProcessHost is still alive before hopping
-// over to the IO thread.
+// Helper method that checks the RenderProcessHost is still alive and checks the
+// latest Safe Browsing pref value on the UI thread before hopping over to the
+// IO thread.
void MaybeCreateSafeBrowsing(
int rph_id,
content::ResourceContext* resource_context,
@@ -49,8 +57,16 @@ void MaybeCreateSafeBrowsing(
if (!render_process_host)
return;
- base::PostTask(
- FROM_HERE, {content::BrowserThread::IO},
+ bool is_safe_browsing_enabled = safe_browsing::IsSafeBrowsingEnabled(
+ *static_cast<BrowserContextImpl*>(
+ render_process_host->GetBrowserContext())
+ ->pref_service());
+
+ if (!is_safe_browsing_enabled)
+ return;
+
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&safe_browsing::MojoSafeBrowsingImpl::MaybeCreate, rph_id,
resource_context, std::move(get_checker_delegate),
std::move(receiver)));
@@ -59,7 +75,7 @@ void MaybeCreateSafeBrowsing(
} // namespace
SafeBrowsingService::SafeBrowsingService(const std::string& user_agent)
- : user_agent_(user_agent), safe_browsing_disabled_(false) {}
+ : user_agent_(user_agent) {}
SafeBrowsingService::~SafeBrowsingService() = default;
@@ -93,7 +109,8 @@ void SafeBrowsingService::Initialize() {
std::unique_ptr<blink::URLLoaderThrottle>
SafeBrowsingService::CreateURLLoaderThrottle(
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
- int frame_tree_node_id) {
+ int frame_tree_node_id,
+ safe_browsing::RealTimeUrlLookupServiceBase* url_lookup_service) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return safe_browsing::BrowserURLLoaderThrottle::Create(
@@ -103,10 +120,7 @@ SafeBrowsingService::CreateURLLoaderThrottle(
},
base::Unretained(this)),
wc_getter, frame_tree_node_id,
- // rt_lookup_service are used to
- // perform real time url check, which is gated by UKM opted in. Since
- // WebLayer currently doesn't support UKM, this feature is not enabled.
- /*rt_lookup_service*/ nullptr);
+ url_lookup_service ? url_lookup_service->GetWeakPtr() : nullptr);
}
std::unique_ptr<content::NavigationThrottle>
@@ -122,8 +136,7 @@ SafeBrowsingService::GetSafeBrowsingUrlCheckerDelegate() {
if (!safe_browsing_url_checker_delegate_) {
safe_browsing_url_checker_delegate_ = new UrlCheckerDelegateImpl(
- GetSafeBrowsingDBManager(), GetSafeBrowsingUIManager(),
- safe_browsing_disabled_);
+ GetSafeBrowsingDBManager(), GetSafeBrowsingUIManager());
}
return safe_browsing_url_checker_delegate_;
@@ -143,7 +156,7 @@ SafeBrowsingUIManager* SafeBrowsingService::GetSafeBrowsingUIManager() {
void SafeBrowsingService::CreateSafeBrowsingUIManager() {
DCHECK(!ui_manager_);
- ui_manager_ = new SafeBrowsingUIManager();
+ ui_manager_ = new SafeBrowsingUIManager(this);
}
void SafeBrowsingService::CreateAndStartSafeBrowsingDBManager() {
@@ -163,8 +176,8 @@ scoped_refptr<network::SharedURLLoaderFactory>
SafeBrowsingService::GetURLLoaderFactoryOnIOThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!shared_url_loader_factory_on_io_) {
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&SafeBrowsingService::CreateURLLoaderFactoryForIO,
base::Unretained(this),
url_loader_factory_on_io_.BindNewPipeAndPassReceiver()));
@@ -198,12 +211,12 @@ void SafeBrowsingService::AddInterface(
base::BindRepeating(
&SafeBrowsingService::GetSafeBrowsingUrlCheckerDelegate,
base::Unretained(this))),
- base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}));
+ content::GetUIThreadTaskRunner({}));
}
void SafeBrowsingService::StopDBManager() {
- base::PostTask(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(&SafeBrowsingService::StopDBManagerOnIOThread,
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&SafeBrowsingService::StopDBManagerOnIOThread,
base::Unretained(this)));
}
@@ -215,24 +228,11 @@ void SafeBrowsingService::StopDBManagerOnIOThread() {
}
}
-void SafeBrowsingService::SetSafeBrowsingDisabled(bool disabled) {
- content::GetIOThreadTaskRunner({})->PostTask(
- FROM_HERE,
- base::BindOnce(&SafeBrowsingService::SetSafeBrowsingDisabledOnIOThread,
- base::Unretained(this), disabled));
-}
-
-void SafeBrowsingService::SetSafeBrowsingDisabledOnIOThread(bool disabled) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (safe_browsing_disabled_ != disabled) {
- safe_browsing_disabled_ = disabled;
- // If there is no safe_browsing_url_checker_delegate_ yet the opt_out
- // setting will be set later during its creation.
- if (safe_browsing_url_checker_delegate_) {
- safe_browsing_url_checker_delegate_->SetSafeBrowsingDisabled(disabled);
- }
- }
+scoped_refptr<network::SharedURLLoaderFactory>
+SafeBrowsingService::GetURLLoaderFactory() {
+ if (!network_context_)
+ return nullptr;
+ return network_context_->GetURLLoaderFactory();
}
} // namespace weblayer
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h
index 3f5506031d5..1de5c0ff021 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h
@@ -29,6 +29,7 @@ class SharedURLLoaderFactory;
namespace safe_browsing {
class UrlCheckerDelegate;
+class RealTimeUrlLookupServiceBase;
class RemoteSafeBrowsingDatabaseManager;
class SafeBrowsingApiHandler;
class SafeBrowsingNetworkContext;
@@ -49,13 +50,14 @@ class SafeBrowsingService {
void Initialize();
std::unique_ptr<blink::URLLoaderThrottle> CreateURLLoaderThrottle(
const base::RepeatingCallback<content::WebContents*()>& wc_getter,
- int frame_tree_node_id);
+ int frame_tree_node_id,
+ safe_browsing::RealTimeUrlLookupServiceBase* url_lookup_service);
std::unique_ptr<content::NavigationThrottle>
CreateSafeBrowsingNavigationThrottle(content::NavigationHandle* handle);
void AddInterface(service_manager::BinderRegistry* registry,
content::RenderProcessHost* render_process_host);
void StopDBManager();
- void SetSafeBrowsingDisabled(bool disabled);
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
private:
SafeBrowsingUIManager* GetSafeBrowsingUIManager();
@@ -72,7 +74,6 @@ class SafeBrowsingService {
void CreateURLLoaderFactoryForIO(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver);
void StopDBManagerOnIOThread();
- void SetSafeBrowsingDisabledOnIOThread(bool disabled);
// The UI manager handles showing interstitials. Accessed on both UI and IO
// thread.
@@ -98,8 +99,6 @@ class SafeBrowsingService {
std::string user_agent_;
- bool safe_browsing_disabled_;
-
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingService);
};
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc
index e2eecc83687..a616dfef1b7 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc
@@ -4,15 +4,28 @@
#include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h"
+#include "components/safe_browsing/core/ping_manager.h"
#include "content/public/browser/browser_thread.h"
#include "weblayer/browser/safe_browsing/safe_browsing_blocking_page.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
#include "weblayer/browser/safe_browsing/safe_browsing_subresource_helper.h"
using content::BrowserThread;
+namespace {
+
+std::string GetProtocolConfigClientName() {
+ // Return a weblayer specific client name.
+ return "weblayer";
+}
+
+} // namespace
+
namespace weblayer {
-SafeBrowsingUIManager::SafeBrowsingUIManager() {
+SafeBrowsingUIManager::SafeBrowsingUIManager(
+ SafeBrowsingService* safe_browsing_service)
+ : safe_browsing_service_(safe_browsing_service) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
@@ -21,8 +34,19 @@ SafeBrowsingUIManager::~SafeBrowsingUIManager() = default;
void SafeBrowsingUIManager::SendSerializedThreatDetails(
const std::string& serialized) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- // TODO(timvolodine): figure out if we want to send any threat reporting here.
- // Note the base implementation does not send anything.
+
+ if (!ping_manager_) {
+ ping_manager_ = ::safe_browsing::PingManager::Create(
+ safe_browsing_service_->GetURLLoaderFactory(),
+ safe_browsing::GetV4ProtocolConfig(GetProtocolConfigClientName(),
+ false /* auto_update */));
+ }
+
+ if (serialized.empty())
+ return;
+
+ DVLOG(1) << "Sending serialized threat details";
+ ping_manager_->ReportThreatDetails(serialized);
}
safe_browsing::BaseBlockingPage*
diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
index 54e1c5136b5..989183c9c02 100644
--- a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
+++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
@@ -14,16 +14,21 @@ class WebContents;
namespace safe_browsing {
class BaseBlockingPage;
+class PingManager;
}
namespace weblayer {
+class SafeBrowsingService;
class SafeBrowsingUIManager : public safe_browsing::BaseUIManager {
public:
// Construction needs to happen on the UI thread.
- SafeBrowsingUIManager();
+ SafeBrowsingUIManager(SafeBrowsingService* safe_browsing_service);
// BaseUIManager overrides.
+
+ // Called on the UI thread by the ThreatDetails with the serialized
+ // protocol buffer, so the service can send it over.
void SendSerializedThreatDetails(const std::string& serialized) override;
protected:
@@ -35,6 +40,11 @@ class SafeBrowsingUIManager : public safe_browsing::BaseUIManager {
const GURL& blocked_url,
const UnsafeResource& unsafe_resource) override;
+ // Provides phishing and malware statistics. Accessed on IO thread.
+ std::unique_ptr<safe_browsing::PingManager> ping_manager_;
+
+ SafeBrowsingService* safe_browsing_service_;
+
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIManager);
};
diff --git a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
index 5eb42c49e92..7ff46a3158c 100644
--- a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
+++ b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -5,7 +5,6 @@
#include "weblayer/browser/safe_browsing/url_checker_delegate_impl.h"
#include "base/bind.h"
-#include "base/task/post_task.h"
#include "components/safe_browsing/core/db/database_manager.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "content/public/browser/browser_task_traits.h"
@@ -16,11 +15,9 @@ namespace weblayer {
UrlCheckerDelegateImpl::UrlCheckerDelegateImpl(
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager,
- scoped_refptr<SafeBrowsingUIManager> ui_manager,
- bool disabled)
+ scoped_refptr<SafeBrowsingUIManager> ui_manager)
: database_manager_(std::move(database_manager)),
ui_manager_(std::move(ui_manager)),
- safe_browsing_disabled_(disabled),
threat_types_(safe_browsing::CreateSBThreatTypeSet(
{safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
@@ -38,8 +35,8 @@ void UrlCheckerDelegateImpl::StartDisplayingBlockingPageHelper(
const net::HttpRequestHeaders& headers,
bool is_main_frame,
bool has_user_gesture) {
- base::PostTask(
- FROM_HERE, {content::BrowserThread::UI},
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(
&UrlCheckerDelegateImpl::StartDisplayingDefaultBlockingPage,
base::Unretained(this), resource));
@@ -61,8 +58,8 @@ void UrlCheckerDelegateImpl::StartDisplayingDefaultBlockingPage(
}
// Report back that it is not ok to proceed with loading the URL.
- base::PostTask(FROM_HERE, {content::BrowserThread::IO},
- base::BindOnce(resource.callback, false /* proceed */,
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(resource.callback, false /* proceed */,
false /* showed_interstitial */));
}
@@ -71,17 +68,13 @@ bool UrlCheckerDelegateImpl::IsUrlWhitelisted(const GURL& url) {
return false;
}
-void UrlCheckerDelegateImpl::SetSafeBrowsingDisabled(bool disabled) {
- safe_browsing_disabled_ = disabled;
-}
-
bool UrlCheckerDelegateImpl::ShouldSkipRequestCheck(
const GURL& original_url,
int frame_tree_node_id,
int render_process_id,
int render_frame_id,
bool originated_from_service_worker) {
- return safe_browsing_disabled_ ? true : false;
+ return false;
}
void UrlCheckerDelegateImpl::NotifySuspiciousSiteDetected(
diff --git a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.h b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
index d59745bed10..c940d7f7c98 100644
--- a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
+++ b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
@@ -23,8 +23,7 @@ class UrlCheckerDelegateImpl : public safe_browsing::UrlCheckerDelegate {
UrlCheckerDelegateImpl(
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
database_manager,
- scoped_refptr<SafeBrowsingUIManager> ui_manager,
- bool disabled);
+ scoped_refptr<SafeBrowsingUIManager> ui_manager);
void SetSafeBrowsingDisabled(bool disabled);
@@ -61,7 +60,6 @@ class UrlCheckerDelegateImpl : public safe_browsing::UrlCheckerDelegate {
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
scoped_refptr<SafeBrowsingUIManager> ui_manager_;
- bool safe_browsing_disabled_;
safe_browsing::SBThreatTypeSet threat_types_;
DISALLOW_COPY_AND_ASSIGN(UrlCheckerDelegateImpl);
diff --git a/chromium/weblayer/browser/site_isolation_browsertest.cc b/chromium/weblayer/browser/site_isolation_browsertest.cc
new file mode 100644
index 00000000000..d10c39f10fc
--- /dev/null
+++ b/chromium/weblayer/browser/site_isolation_browsertest.cc
@@ -0,0 +1,256 @@
+// 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.
+
+#include "base/base_switches.h"
+#include "base/files/file_path.h"
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "components/site_isolation/features.h"
+#include "components/site_isolation/pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/site_isolation_policy.h"
+#include "content/public/common/content_client.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/content_browser_client_impl.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/browser/tab_impl.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/weblayer_browser_test.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+using testing::IsEmpty;
+using testing::UnorderedElementsAre;
+
+class SiteIsolationBrowserTest : public WebLayerBrowserTest {
+ public:
+ SiteIsolationBrowserTest() {
+ feature_list_.InitWithFeaturesAndParameters(
+ {{site_isolation::features::kSitePerProcessOnlyForHighMemoryClients,
+ {{site_isolation::features::
+ kSitePerProcessOnlyForHighMemoryClientsParamName,
+ "128"}}},
+ {site_isolation::features::kSiteIsolationForPasswordSites, {}}},
+ {});
+ }
+
+ std::vector<std::string> GetSavedIsolatedSites() {
+ PrefService* prefs =
+ user_prefs::UserPrefs::Get(GetProfile()->GetBrowserContext());
+ auto* list =
+ prefs->GetList(site_isolation::prefs::kUserTriggeredIsolatedOrigins);
+ std::vector<std::string> sites;
+ for (const base::Value& value : list->GetList())
+ sites.push_back(value.GetString());
+ return sites;
+ }
+
+ // WebLayerBrowserTest:
+ void SetUpOnMainThread() override {
+ original_client_ = content::SetBrowserClientForTesting(&browser_client_);
+ host_resolver()->AddRule("*", "127.0.0.1");
+ embedded_test_server()->ServeFilesFromSourceDirectory("weblayer/test/data");
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ ASSERT_FALSE(
+ content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
+ ASSERT_TRUE(
+ content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled());
+ }
+
+ void TearDownOnMainThread() override {
+ content::SetBrowserClientForTesting(original_client_);
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch("ignore-certificate-errors");
+
+ // This way the test always sees the same amount of physical memory
+ // (kLowMemoryDeviceThresholdMB = 512MB), regardless of how much memory is
+ // available in the testing environment.
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableLowEndDeviceMode);
+ EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB());
+ }
+
+ content::WebContents* GetWebContents() {
+ return static_cast<TabImpl*>(shell()->tab())->web_contents();
+ }
+
+ private:
+ // A browser client which forces off strict site isolation, so the test can
+ // assume password isolation is enabled.
+ class SiteIsolationContentBrowserClient : public ContentBrowserClientImpl {
+ public:
+ SiteIsolationContentBrowserClient() : ContentBrowserClientImpl(nullptr) {}
+
+ bool ShouldEnableStrictSiteIsolation() override { return false; }
+ };
+
+ SiteIsolationContentBrowserClient browser_client_;
+ content::ContentBrowserClient* original_client_ = nullptr;
+ base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
+ SiteIsIsolatedAfterEnteringPassword) {
+ GURL url = embedded_test_server()->GetURL("sub.foo.com",
+ "/simple_password_form.html");
+ NavigateAndWaitForCompletion(url, shell());
+ content::WebContents* contents = GetWebContents();
+
+ // foo.com should not be isolated to start with. Verify that a cross-site
+ // iframe does not become an OOPIF.
+ EXPECT_FALSE(
+ contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
+ std::string kAppendIframe = R"(
+ var i = document.createElement('iframe');
+ i.id = 'child';
+ document.body.appendChild(i);)";
+ EXPECT_TRUE(content::ExecJs(contents, kAppendIframe));
+ GURL bar_url(embedded_test_server()->GetURL("bar.com", "/simple_page.html"));
+ EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url));
+ content::RenderFrameHost* child = ChildFrameAt(contents->GetMainFrame(), 0);
+ EXPECT_FALSE(child->IsCrossProcessSubframe());
+
+ // Fill a form and submit through a <input type="submit"> button.
+ content::TestNavigationObserver observer(contents);
+ std::string kFillAndSubmit =
+ "document.getElementById('username_field').value = 'temp';"
+ "document.getElementById('password_field').value = 'random';"
+ "document.getElementById('input_submit_button').click()";
+ EXPECT_TRUE(content::ExecJs(contents, kFillAndSubmit));
+ observer.Wait();
+
+ // Since there were no script references from other windows, we should've
+ // swapped BrowsingInstances and put the result of the form submission into a
+ // dedicated process, locked to foo.com. Check that a cross-site iframe now
+ // becomes an OOPIF.
+ EXPECT_TRUE(
+ contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
+ EXPECT_TRUE(ExecJs(contents, kAppendIframe));
+ EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url));
+ child = ChildFrameAt(contents->GetMainFrame(), 0);
+ EXPECT_TRUE(child->IsCrossProcessSubframe());
+}
+
+// TODO(crbug.com/654704): Android does not support PRE_ tests.
+#if !defined(OS_ANDROID)
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
+ PRE_IsolatedSitesPersistAcrossRestarts) {
+ // There shouldn't be any saved isolated origins to start with.
+ EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty());
+
+ // Isolate saved.com and saved2.com persistently.
+ GURL saved_url =
+ embedded_test_server()->GetURL("saved.com", "/simple_page.html");
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ GURL saved2_url =
+ embedded_test_server()->GetURL("saved2.com", "/simple_page.html");
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved2_url);
+
+ NavigateAndWaitForCompletion(saved_url, shell());
+ EXPECT_TRUE(GetWebContents()
+ ->GetMainFrame()
+ ->GetSiteInstance()
+ ->RequiresDedicatedProcess());
+
+ // Check that saved.com and saved2.com were saved to disk.
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("http://saved.com", "http://saved2.com"));
+}
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
+ IsolatedSitesPersistAcrossRestarts) {
+ // Check that saved.com and saved2.com are still saved to disk.
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("http://saved.com", "http://saved2.com"));
+
+ // Check that these sites utilize a dedicated process after restarting, but a
+ // non-isolated foo.com URL does not.
+ GURL saved_url =
+ embedded_test_server()->GetURL("saved.com", "/simple_page.html");
+ GURL saved2_url =
+ embedded_test_server()->GetURL("saved2.com", "/simple_page2.html");
+ GURL foo_url =
+ embedded_test_server()->GetURL("foo.com", "/simple_page3.html");
+ NavigateAndWaitForCompletion(saved_url, shell());
+ content::WebContents* contents = GetWebContents();
+ EXPECT_TRUE(
+ contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
+ NavigateAndWaitForCompletion(saved2_url, shell());
+ EXPECT_TRUE(
+ contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
+ NavigateAndWaitForCompletion(foo_url, shell());
+ EXPECT_FALSE(
+ contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess());
+}
+#endif
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, IsolatedSiteIsSavedOnlyOnce) {
+ GURL saved_url =
+ embedded_test_server()->GetURL("saved.com", "/simple_page.html");
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("http://saved.com"));
+}
+
+// Verify that serving a Clear-Site-Data header does not clear saved isolated
+// sites. Saved isolated sites should only be cleared by user-initiated actions.
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
+ ClearSiteDataHeaderDoesNotClearSavedIsolatedSites) {
+ // Start an HTTPS server, as Clear-Site-Data is only available on HTTPS URLs.
+ net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+ https_server.AddDefaultHandlers(
+ base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+ ASSERT_TRUE(https_server.Start());
+
+ // Isolate saved.com and verify it's been saved to disk.
+ GURL saved_url = https_server.GetURL("saved.com", "/clear_site_data.html");
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("https://saved.com"));
+
+ // Navigate to a URL that serves a Clear-Site-Data header for cache, cookies,
+ // and DOM storage. This is the most that a Clear-Site-Data header could
+ // clear, and this should not clear saved isolated sites.
+ NavigateAndWaitForCompletion(saved_url, shell());
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("https://saved.com"));
+}
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest,
+ ExplicitClearBrowsingDataClearsSavedIsolatedSites) {
+ GURL saved_url =
+ embedded_test_server()->GetURL("saved.com", "/simple_page.html");
+ content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(),
+ saved_url);
+ EXPECT_THAT(GetSavedIsolatedSites(),
+ UnorderedElementsAre("http://saved.com"));
+
+ base::RunLoop run_loop;
+ base::Time now = base::Time::Now();
+ GetProfile()->ClearBrowsingData({BrowsingDataType::COOKIES_AND_SITE_DATA},
+ now - base::TimeDelta::FromDays(1), now,
+ run_loop.QuitClosure());
+ run_loop.Run();
+
+ EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty());
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/site_isolation_policy_unittest.cc b/chromium/weblayer/browser/site_isolation_policy_unittest.cc
new file mode 100644
index 00000000000..1ec3c0f711d
--- /dev/null
+++ b/chromium/weblayer/browser/site_isolation_policy_unittest.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include "components/site_isolation/site_isolation_policy.h"
+
+#include "base/base_switches.h"
+#include "base/system/sys_info.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "components/site_isolation/features.h"
+#include "components/site_isolation/preloaded_isolated_origins.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/site_isolation_policy.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace weblayer {
+namespace {
+using testing::UnorderedElementsAreArray;
+
+// Some command-line switches override field trials - the tests need to be
+// skipped in this case.
+bool ShouldSkipBecauseOfConflictingCommandLineSwitches() {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSitePerProcess))
+ return true;
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableSiteIsolation))
+ return true;
+
+ return false;
+}
+
+} // namespace
+
+class SiteIsolationPolicyTest : public testing::Test {
+ public:
+ SiteIsolationPolicyTest() = default;
+
+ void SetUp() override {
+ // This way the test always sees the same amount of physical memory
+ // (kLowMemoryDeviceThresholdMB = 512MB), regardless of how much memory is
+ // available in the testing environment.
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableLowEndDeviceMode);
+ EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB());
+ }
+
+ void SetMemoryThreshold(const std::string& threshold) {
+ threshold_feature_.InitAndEnableFeatureWithParameters(
+ site_isolation::features::kSitePerProcessOnlyForHighMemoryClients,
+ {{site_isolation::features::
+ kSitePerProcessOnlyForHighMemoryClientsParamName,
+ threshold}});
+ }
+
+ private:
+ content::BrowserTaskEnvironment task_environment_;
+ base::test::ScopedFeatureList threshold_feature_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteIsolationPolicyTest);
+};
+
+TEST_F(SiteIsolationPolicyTest, NoIsolationBelowMemoryThreshold) {
+ if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
+ return;
+
+ SetMemoryThreshold("768");
+ EXPECT_FALSE(
+ content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
+ EXPECT_FALSE(
+ content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());
+}
+
+TEST_F(SiteIsolationPolicyTest, IsolationAboveMemoryThreshold) {
+ if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
+ return;
+
+ SetMemoryThreshold("128");
+ // Android should only use the preloaded origin list, while desktop should
+ // isolate all sites.
+#if defined(OS_ANDROID)
+ EXPECT_FALSE(
+ content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
+ EXPECT_TRUE(
+ content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());
+#else
+ EXPECT_TRUE(content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites());
+ EXPECT_FALSE(
+ content::SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled());
+#endif
+}
+
+TEST_F(SiteIsolationPolicyTest, IsolatedOriginsContainPreloadedOrigins) {
+ if (ShouldSkipBecauseOfConflictingCommandLineSwitches())
+ return;
+
+ content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
+
+ std::vector<url::Origin> expected_embedder_origins =
+ site_isolation::GetBrowserSpecificBuiltInIsolatedOrigins();
+ auto* cpsp = content::ChildProcessSecurityPolicy::GetInstance();
+ std::vector<url::Origin> isolated_origins = cpsp->GetIsolatedOrigins();
+ EXPECT_THAT(expected_embedder_origins,
+ UnorderedElementsAreArray(isolated_origins));
+}
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/ssl_browsertest.cc b/chromium/weblayer/browser/ssl_browsertest.cc
index 82973b2b143..0ef9fa45322 100644
--- a/chromium/weblayer/browser/ssl_browsertest.cc
+++ b/chromium/weblayer/browser/ssl_browsertest.cc
@@ -7,12 +7,15 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/optional.h"
+#include "base/scoped_observer.h"
#include "build/build_config.h"
#include "components/network_time/network_time_tracker.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/weblayer_security_blocking_page_factory.h"
+#include "weblayer/public/browser.h"
+#include "weblayer/public/browser_observer.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/interstitial_utils.h"
#include "weblayer/test/load_completion_observer.h"
@@ -20,6 +23,37 @@
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
+namespace {
+
+#if defined(OS_ANDROID)
+// Waits for a new tab to be created, and then load |url|.
+class NewTabWaiter : public BrowserObserver {
+ public:
+ NewTabWaiter(Browser* browser, const GURL& url) : url_(url) {
+ observer_.Add(browser);
+ }
+
+ void OnTabAdded(Tab* tab) override {
+ navigation_observer_ = std::make_unique<TestNavigationObserver>(
+ url_, TestNavigationObserver::NavigationEvent::kStart, tab);
+ run_loop_.Quit();
+ }
+
+ void Wait() {
+ if (!navigation_observer_)
+ run_loop_.Run();
+ navigation_observer_->Wait();
+ }
+
+ private:
+ GURL url_;
+ std::unique_ptr<TestNavigationObserver> navigation_observer_;
+ base::RunLoop run_loop_;
+ ScopedObserver<Browser, BrowserObserver> observer_{this};
+};
+#endif
+
+} // namespace
class SSLBrowserTest : public WebLayerBrowserTest {
public:
@@ -166,13 +200,12 @@ class SSLBrowserTest : public WebLayerBrowserTest {
// Note: The embedded test server cannot actually load the captive portal
// login URL, so simply detect the start of the navigation to the page.
- TestNavigationObserver navigation_observer(
- WebLayerSecurityBlockingPageFactory::
- GetCaptivePortalLoginPageUrlForTesting(),
- TestNavigationObserver::NavigationEvent::kStart, shell());
+ NewTabWaiter waiter(shell()->browser(),
+ WebLayerSecurityBlockingPageFactory::
+ GetCaptivePortalLoginPageUrlForTesting());
ExecuteScript(shell(), "window.certificateErrorPageController.openLogin();",
false /*use_separate_isolate*/);
- navigation_observer.Wait();
+ waiter.Wait();
}
#endif
diff --git a/chromium/weblayer/browser/system_network_context_manager.cc b/chromium/weblayer/browser/system_network_context_manager.cc
index bb6a56918de..2f7900fc33d 100644
--- a/chromium/weblayer/browser/system_network_context_manager.cc
+++ b/chromium/weblayer/browser/system_network_context_manager.cc
@@ -6,7 +6,6 @@
#include "build/build_config.h"
#include "components/variations/net/variations_http_headers.h"
-#include "content/public/browser/cors_exempt_headers.h"
#include "content/public/browser/network_service_instance.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -52,9 +51,10 @@ SystemNetworkContextManager::CreateDefaultNetworkContextParams(
const std::string& user_agent) {
network::mojom::NetworkContextParamsPtr network_context_params =
network::mojom::NetworkContextParams::New();
+ network_context_params->cert_verifier_params = content::GetCertVerifierParams(
+ network::mojom::CertVerifierCreationParams::New());
ConfigureDefaultNetworkContextParams(network_context_params.get(),
user_agent);
- content::UpdateCorsExemptHeader(network_context_params.get());
variations::UpdateCorsExemptHeaderForVariations(network_context_params.get());
return network_context_params;
}
@@ -94,8 +94,6 @@ SystemNetworkContextManager::GetSystemNetworkContext() {
void SystemNetworkContextManager::OnNetworkServiceCreated(
network::mojom::NetworkService* network_service) {
- // The system NetworkContext must be created first, since it sets
- // |primary_network_context| to true.
system_network_context_.reset();
network_service->CreateNetworkContext(
system_network_context_.BindNewPipeAndPassReceiver(),
@@ -108,7 +106,6 @@ SystemNetworkContextManager::CreateSystemNetworkContextManagerParams() {
CreateDefaultNetworkContextParams(user_agent_);
network_context_params->context_name = std::string("system");
- network_context_params->primary_network_context = true;
return network_context_params;
}
diff --git a/chromium/weblayer/browser/tab_impl.cc b/chromium/weblayer/browser/tab_impl.cc
index 605821fcacc..052dbcb9517 100644
--- a/chromium/weblayer/browser/tab_impl.cc
+++ b/chromium/weblayer/browser/tab_impl.cc
@@ -9,22 +9,32 @@
#include "base/auto_reset.h"
#include "base/guid.h"
#include "base/logging.h"
-#include "base/task/post_task.h"
+#include "base/no_destructor.h"
#include "base/task/thread_pool.h"
+#include "base/time/default_tick_clock.h"
#include "cc/layers/layer.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_provider.h"
+#include "components/blocked_content/popup_blocker.h"
+#include "components/blocked_content/popup_blocker_tab_helper.h"
+#include "components/blocked_content/popup_opener_tab_helper.h"
+#include "components/blocked_content/popup_tracker.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/client_hints/browser/client_hints.h"
#include "components/content_settings/browser/tab_specific_content_settings.h"
#include "components/find_in_page/find_tab_helper.h"
#include "components/find_in_page/find_types.h"
+#include "components/js_injection/browser/js_communication_host.h"
+#include "components/js_injection/browser/web_message_host.h"
+#include "components/js_injection/browser/web_message_host_factory.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_result.h"
#include "components/prefs/pref_service.h"
#include "components/sessions/content/session_tab_helper.h"
+#include "components/translate/core/browser/translate_manager.h"
+#include "components/ukm/content/source_url_recorder.h"
#include "components/webrtc/media_stream_devices_controller.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -38,23 +48,32 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/web_preferences.h"
#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
+#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
#include "ui/base/window_open_disposition.h"
#include "weblayer/browser/autofill_client_impl.h"
+#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/content_browser_client_impl.h"
#include "weblayer/browser/file_select_helper.h"
#include "weblayer/browser/host_content_settings_map_factory.h"
#include "weblayer/browser/i18n_util.h"
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/js_communication/web_message_host_factory_wrapper.h"
#include "weblayer/browser/navigation_controller_impl.h"
#include "weblayer/browser/page_load_metrics_initialize.h"
+#include "weblayer/browser/password_manager_driver_factory.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
#include "weblayer/browser/persistence/browser_persister.h"
+#include "weblayer/browser/popup_navigation_delegate_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_specific_content_settings_delegate.h"
#include "weblayer/browser/translate_client_impl.h"
+#include "weblayer/browser/weblayer_features.h"
#include "weblayer/common/isolated_world_ids.h"
#include "weblayer/public/fullscreen_delegate.h"
+#include "weblayer/public/js_communication/web_message.h"
+#include "weblayer/public/js_communication/web_message_host_factory.h"
#include "weblayer/public/new_tab_delegate.h"
#include "weblayer/public/tab_observer.h"
@@ -64,10 +83,11 @@
#if defined(OS_ANDROID)
#include "base/android/callback_android.h"
+#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/json/json_writer.h"
#include "base/trace_event/trace_event.h"
-#include "components/autofill/android/autofill_provider_android.h"
+#include "components/autofill/android/provider/autofill_provider_android.h"
#include "components/embedder_support/android/contextmenu/context_menu_builder.h"
#include "components/embedder_support/android/delegate/color_chooser_android.h"
#include "components/javascript_dialogs/tab_modal_dialog_manager.h" // nogncheck
@@ -76,8 +96,10 @@
#include "weblayer/browser/browser_controls_container_view.h"
#include "weblayer/browser/browser_controls_navigation_state_handler.h"
#include "weblayer/browser/controls_visibility_reason.h"
+#include "weblayer/browser/http_auth_handler_impl.h"
#include "weblayer/browser/java/jni/TabImpl_jni.h"
#include "weblayer/browser/javascript_tab_modal_dialog_manager_delegate_android.h"
+#include "weblayer/browser/js_communication/web_message_host_factory_proxy.h"
#include "weblayer/browser/weblayer_factory_impl_android.h"
#include "weblayer/browser/webrtc/media_stream_manager.h"
#endif
@@ -98,6 +120,9 @@ namespace weblayer {
namespace {
+// Maximum size of data when calling SetData().
+constexpr int kMaxDataSize = 4096;
+
#if defined(OS_ANDROID)
bool g_system_autofill_disabled_for_testing = false;
@@ -127,16 +152,11 @@ NewTabType NewTabTypeFromWindowDisposition(WindowOpenDisposition disposition) {
// Opens a captive portal login page in |web_contents|.
void OpenCaptivePortalLoginTabInWebContents(
content::WebContents* web_contents) {
- // In Chrome this opens in a new tab, but WebLayer's TabImpl has no support
- // for opening new tabs (its OpenURLFromTab() method DCHECKs if the
- // disposition is not |CURRENT_TAB|).
- // TODO(crbug.com/1047130): Revisit if TabImpl gets support for opening URLs
- // in new tabs.
content::OpenURLParams params(
CaptivePortalServiceFactory::GetForBrowserContext(
web_contents->GetBrowserContext())
->test_url(),
- content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+ content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
web_contents->OpenURL(params);
}
@@ -147,12 +167,10 @@ void OpenCaptivePortalLoginTabInWebContents(
constexpr int kWebContentsUserDataKey = 0;
struct UserData : public base::SupportsUserData::Data {
- TabImpl* controller = nullptr;
+ TabImpl* tab = nullptr;
};
#if defined(OS_ANDROID)
-Tab* g_last_tab;
-
void HandleJavaScriptResult(const ScopedJavaGlobalRef<jobject>& callback,
base::Value result) {
std::string json;
@@ -179,8 +197,8 @@ void ConvertToJavaBitmapBackgroundThread(
// Make sure to only pass ScopedJavaGlobalRef between threads.
ScopedJavaGlobalRef<jobject> java_bitmap = ScopedJavaGlobalRef<jobject>(
gfx::ConvertToJavaBitmap(&bitmap, gfx::OomBehavior::kReturnNullOnOom));
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(std::move(callback), std::move(java_bitmap)));
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), std::move(java_bitmap)));
}
void OnScreenShotCaptured(const ScopedJavaGlobalRef<jobject>& value_callback,
@@ -204,9 +222,26 @@ void OnScreenShotCaptured(const ScopedJavaGlobalRef<jobject>& value_callback,
#endif // OS_ANDROID
+std::set<TabImpl*>& GetTabs() {
+ static base::NoDestructor<std::set<TabImpl*>> s_all_tab_impl;
+ return *s_all_tab_impl;
+}
+
} // namespace
#if defined(OS_ANDROID)
+
+static ScopedJavaLocalRef<jobject> JNI_TabImpl_FromWebContents(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& j_web_contents) {
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(j_web_contents);
+ TabImpl* tab = TabImpl::FromWebContents(web_contents);
+ if (tab)
+ return ScopedJavaLocalRef<jobject>(tab->GetJavaTab());
+ return nullptr;
+}
+
TabImpl::TabImpl(ProfileImpl* profile, const JavaParamRef<jobject>& java_impl)
: TabImpl(profile) {
java_impl_ = java_impl;
@@ -219,9 +254,7 @@ TabImpl::TabImpl(ProfileImpl* profile,
: profile_(profile),
web_contents_(std::move(web_contents)),
guid_(guid.empty() ? base::GenerateGUID() : guid) {
-#if defined(OS_ANDROID)
- g_last_tab = this;
-#endif
+ GetTabs().insert(this);
if (web_contents_) {
// This code path is hit when the page requests a new tab, which should
// only be possible from the same profile.
@@ -245,7 +278,7 @@ TabImpl::TabImpl(ProfileImpl* profile,
&TabImpl::UpdateRendererPrefs, base::Unretained(this), true));
std::unique_ptr<UserData> user_data = std::make_unique<UserData>();
- user_data->controller = this;
+ user_data->tab = this;
web_contents_->SetUserData(&kWebContentsUserDataKey, std::move(user_data));
web_contents_->SetDelegate(this);
@@ -253,13 +286,14 @@ TabImpl::TabImpl(ProfileImpl* profile,
navigation_controller_ = std::make_unique<NavigationControllerImpl>(this);
+#if defined(OS_ANDROID)
+ InfoBarService::CreateForWebContents(web_contents_.get());
+#endif
+
find_in_page::FindTabHelper::CreateForWebContents(web_contents_.get());
GetFindTabHelper()->AddObserver(this);
- // TODO(crbug.com/1072334): Resolve incorporation of translate in incognito
- // mode.
- if (!web_contents_->GetBrowserContext()->IsOffTheRecord())
- TranslateClientImpl::CreateForWebContents(web_contents_.get());
+ TranslateClientImpl::CreateForWebContents(web_contents_.get());
sessions::SessionTabHelper::CreateForWebContents(
web_contents_.get(),
@@ -278,10 +312,19 @@ TabImpl::TabImpl(ProfileImpl* profile,
content_settings::TabSpecificContentSettings::CreateForWebContents(
web_contents_.get(), std::make_unique<TabSpecificContentSettingsDelegate>(
web_contents_.get()));
+ blocked_content::PopupBlockerTabHelper::CreateForWebContents(
+ web_contents_.get());
+ blocked_content::PopupOpenerTabHelper::CreateForWebContents(
+ web_contents_.get(), base::DefaultTickClock::GetInstance(),
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ web_contents_->GetBrowserContext()));
+ PasswordManagerDriverFactory::CreateForWebContents(web_contents_.get());
InitializePageLoadMetricsForWebContents(web_contents_.get());
+ ukm::InitializeSourceUrlRecorderForWebContents(web_contents_.get());
#if defined(OS_ANDROID)
+ InfoBarService::CreateForWebContents(web_contents_.get());
javascript_dialogs::TabModalDialogManager::CreateForWebContents(
web_contents_.get(),
std::make_unique<JavaScriptTabModalDialogManagerDelegateAndroid>(
@@ -318,6 +361,7 @@ TabImpl::~TabImpl() {
Observe(nullptr);
web_contents_->SetDelegate(nullptr);
web_contents_.reset();
+ GetTabs().erase(this);
}
// static
@@ -325,9 +369,22 @@ TabImpl* TabImpl::FromWebContents(content::WebContents* web_contents) {
if (!web_contents)
return nullptr;
- return reinterpret_cast<UserData*>(
- web_contents->GetUserData(&kWebContentsUserDataKey))
- ->controller;
+ UserData* user_data = reinterpret_cast<UserData*>(
+ web_contents->GetUserData(&kWebContentsUserDataKey));
+ return user_data ? user_data->tab : nullptr;
+}
+
+// static
+std::set<TabImpl*> TabImpl::GetAllTabImpl() {
+ return GetTabs();
+}
+
+void TabImpl::AddDataObserver(DataObserver* observer) {
+ data_observers_.AddObserver(observer);
+}
+
+void TabImpl::RemoveDataObserver(DataObserver* observer) {
+ data_observers_.RemoveObserver(observer);
}
void TabImpl::SetErrorPageDelegate(ErrorPageDelegate* delegate) {
@@ -388,6 +445,35 @@ const std::string& TabImpl::GetGuid() {
return guid_;
}
+void TabImpl::SetData(const std::map<std::string, std::string>& data) {
+ bool result = SetDataInternal(data);
+ DCHECK(result) << "Data given to SetData() was too large.";
+}
+
+const std::map<std::string, std::string>& TabImpl::GetData() {
+ return data_;
+}
+
+base::string16 TabImpl::AddWebMessageHostFactory(
+ std::unique_ptr<WebMessageHostFactory> factory,
+ const base::string16& js_object_name,
+ const std::vector<std::string>& allowed_origin_rules) {
+ if (!js_communication_host_) {
+ js_communication_host_ =
+ std::make_unique<js_injection::JsCommunicationHost>(
+ web_contents_.get());
+ }
+ return js_communication_host_->AddWebMessageHostFactory(
+ std::make_unique<WebMessageHostFactoryWrapper>(std::move(factory)),
+ js_object_name, allowed_origin_rules);
+}
+
+void TabImpl::RemoveWebMessageHostFactory(
+ const base::string16& js_object_name) {
+ if (js_communication_host_)
+ js_communication_host_->RemoveWebMessageHostFactory(js_object_name);
+}
+
void TabImpl::ExecuteScriptWithUserGestureForTests(
const base::string16& script) {
web_contents_->GetMainFrame()->ExecuteJavaScriptWithUserGestureForTests(
@@ -430,6 +516,28 @@ void TabImpl::ShowContextMenu(const content::ContextMenuParams& params) {
#endif
}
+void TabImpl::ShowHttpAuthPrompt(HttpAuthHandlerImpl* auth_handler) {
+ CHECK(!auth_handler_);
+ auth_handler_ = auth_handler;
+#if defined(OS_ANDROID)
+ JNIEnv* env = AttachCurrentThread();
+ GURL url = auth_handler_->url();
+ Java_TabImpl_showHttpAuthPrompt(
+ env, java_impl_, base::android::ConvertUTF8ToJavaString(env, url.host()),
+ base::android::ConvertUTF8ToJavaString(env, url.spec()));
+#endif
+}
+
+void TabImpl::CloseHttpAuthPrompt() {
+ if (!auth_handler_)
+ return;
+ auth_handler_ = nullptr;
+#if defined(OS_ANDROID)
+ JNIEnv* env = AttachCurrentThread();
+ Java_TabImpl_closeHttpAuthPrompt(env, java_impl_);
+#endif
+}
+
#if defined(OS_ANDROID)
// static
void TabImpl::DisableAutofillSystemIntegrationForTesting() {
@@ -444,13 +552,10 @@ static jlong JNI_TabImpl_CreateTab(JNIEnv* env,
}
static void JNI_TabImpl_DeleteTab(JNIEnv* env, jlong tab) {
- std::unique_ptr<Tab> owned_tab;
TabImpl* tab_impl = reinterpret_cast<TabImpl*>(tab);
DCHECK(tab_impl);
- if (tab_impl->browser())
- owned_tab = tab_impl->browser()->RemoveTab(tab_impl);
- else
- owned_tab.reset(tab_impl);
+ DCHECK(tab_impl->browser());
+ tab_impl->browser()->DestroyTab(tab_impl);
}
ScopedJavaLocalRef<jobject> TabImpl::GetWebContents(JNIEnv* env) {
@@ -571,6 +676,8 @@ void TabImpl::UpdateBrowserControlsStateImpl(
content::BrowserControlsState old_state,
bool animate) {
current_browser_controls_state_ = new_state;
+ if (base::FeatureList::IsEnabled(kImmediatelyHideBrowserControlsForTest))
+ animate = false;
web_contents_->GetMainFrame()->UpdateBrowserControlsState(new_state,
old_state, animate);
}
@@ -596,24 +703,116 @@ void TabImpl::CaptureScreenShot(
base::BindOnce(&OnScreenShotCaptured,
ScopedJavaGlobalRef<jobject>(value_callback)));
}
+
+jboolean TabImpl::SetData(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobjectArray>& data) {
+ std::vector<std::string> flattened_map;
+ base::android::AppendJavaStringArrayToStringVector(env, data, &flattened_map);
+ std::map<std::string, std::string> data_map;
+ for (size_t i = 0; i < flattened_map.size(); i += 2) {
+ data_map.insert({flattened_map[i], flattened_map[i + 1]});
+ }
+ return SetDataInternal(data_map);
+}
+
+base::android::ScopedJavaLocalRef<jobjectArray> TabImpl::GetData(JNIEnv* env) {
+ std::vector<std::string> flattened_map;
+ for (const auto& kv : data_) {
+ flattened_map.push_back(kv.first);
+ flattened_map.push_back(kv.second);
+ }
+ return base::android::ToJavaArrayOfStrings(env, flattened_map);
+}
+
jboolean TabImpl::IsRendererControllingBrowserControlsOffsets(JNIEnv* env) {
return browser_controls_navigation_state_handler_
->IsRendererControllingOffsets();
}
+void TabImpl::SetHttpAuth(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& username,
+ const base::android::JavaParamRef<jstring>& password) {
+ auth_handler_->Proceed(
+ base::android::ConvertJavaStringToUTF16(env, username),
+ base::android::ConvertJavaStringToUTF16(env, password));
+ CloseHttpAuthPrompt();
+}
+
+void TabImpl::CancelHttpAuth(JNIEnv* env) {
+ auth_handler_->Cancel();
+ CloseHttpAuthPrompt();
+}
+
+base::android::ScopedJavaLocalRef<jstring> TabImpl::RegisterWebMessageCallback(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& js_object_name,
+ const base::android::JavaParamRef<jobjectArray>& js_origins,
+ const base::android::JavaParamRef<jobject>& client) {
+ auto proxy = std::make_unique<WebMessageHostFactoryProxy>(client);
+ std::vector<std::string> origins;
+ base::android::AppendJavaStringArrayToStringVector(env, js_origins, &origins);
+ base::string16 result = AddWebMessageHostFactory(
+ std::move(proxy),
+ base::android::ConvertJavaStringToUTF16(env, js_object_name), origins);
+ return base::android::ConvertUTF16ToJavaString(env, result);
+}
+
+void TabImpl::UnregisterWebMessageCallback(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& js_object_name) {
+ base::string16 name;
+ base::android::ConvertJavaStringToUTF16(env, js_object_name, &name);
+ RemoveWebMessageHostFactory(name);
+}
+
+jboolean TabImpl::CanTranslate(JNIEnv* env) {
+ return TranslateClientImpl::FromWebContents(web_contents())
+ ->GetTranslateManager()
+ ->CanManuallyTranslate();
+}
+
+void TabImpl::ShowTranslateUi(JNIEnv* env) {
+ TranslateClientImpl::FromWebContents(web_contents())
+ ->ManualTranslateWhenReady();
+}
#endif // OS_ANDROID
content::WebContents* TabImpl::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
- if (params.disposition != WindowOpenDisposition::CURRENT_TAB) {
- NOTIMPLEMENTED();
- return nullptr;
+ if (blocked_content::ConsiderForPopupBlocking(params.disposition)) {
+ bool blocked = blocked_content::MaybeBlockPopup(
+ source, nullptr,
+ std::make_unique<PopupNavigationDelegateImpl>(
+ params, source, nullptr),
+ &params, blink::mojom::WindowFeatures(),
+ HostContentSettingsMapFactory::GetForBrowserContext(
+ source->GetBrowserContext())) == nullptr;
+ if (blocked)
+ return nullptr;
+ }
+
+ if (params.disposition == WindowOpenDisposition::CURRENT_TAB) {
+ source->GetController().LoadURLWithParams(
+ content::NavigationController::LoadURLParams(params));
+ return source;
}
- source->GetController().LoadURLWithParams(
+ // All URLs not opening in the current tab will get a new tab.
+ std::unique_ptr<content::WebContents> new_tab_contents =
+ content::WebContents::Create(content::WebContents::CreateParams(
+ web_contents()->GetBrowserContext()));
+ content::WebContents* new_tab_contents_raw = new_tab_contents.get();
+ bool was_blocked = false;
+ AddNewContents(web_contents(), std::move(new_tab_contents), params.url,
+ params.disposition, {}, params.user_gesture, &was_blocked);
+ if (was_blocked)
+ return nullptr;
+ new_tab_contents_raw->GetController().LoadURLWithParams(
content::NavigationController::LoadURLParams(params));
- return source;
+ return new_tab_contents_raw;
}
void TabImpl::ShowRepostFormWarningDialog(content::WebContents* source) {
@@ -681,10 +880,9 @@ void TabImpl::RunFileChooser(
int TabImpl::GetTopControlsHeight() {
#if defined(OS_ANDROID)
- int height = top_controls_container_view_
- ? top_controls_container_view_->GetControlsHeight()
- : 0;
- return height;
+ return top_controls_container_view_
+ ? top_controls_container_view_->GetControlsHeight()
+ : 0;
#else
return 0;
#endif
@@ -747,8 +945,7 @@ bool TabImpl::CheckMediaAccessPermission(
}
void TabImpl::EnterFullscreenModeForTab(
- content::WebContents* web_contents,
- const GURL& origin,
+ content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) {
// TODO: support |options|.
is_fullscreen_ = true;
@@ -791,12 +988,19 @@ void TabImpl::AddNewContents(content::WebContents* source,
const gfx::Rect& initial_rect,
bool user_gesture,
bool* was_blocked) {
- if (!new_tab_delegate_)
+ if (!new_tab_delegate_) {
+ *was_blocked = true;
return;
+ }
+
+ // At this point the |new_contents| is beyond the popup blocker, but we use
+ // the same logic for determining if the popup tracker needs to be attached.
+ if (source && blocked_content::ConsiderForPopupBlocking(disposition)) {
+ blocked_content::PopupTracker::CreateForWebContents(new_contents.get(),
+ source, disposition);
+ }
- std::unique_ptr<Tab> tab =
- std::make_unique<TabImpl>(profile_, std::move(new_contents));
- new_tab_delegate_->OnNewTab(std::move(tab),
+ new_tab_delegate_->OnNewTab(browser_->CreateTab(std::move(new_contents)),
NewTabTypeFromWindowDisposition(disposition));
}
@@ -820,7 +1024,7 @@ void TabImpl::CloseContents(content::WebContents* source) {
// return.
}
#else
- browser_->RemoveTab(this);
+ browser_->DestroyTab(this);
#endif
}
@@ -952,16 +1156,6 @@ void TabImpl::SetBrowserControlsConstraint(
}
#endif
-std::unique_ptr<Tab> Tab::Create(Profile* profile) {
- return std::make_unique<TabImpl>(static_cast<ProfileImpl*>(profile));
-}
-
-#if defined(OS_ANDROID)
-Tab* Tab::GetLastTabForTesting() {
- return g_last_tab;
-}
-#endif
-
void TabImpl::InitializeAutofillForTests(
std::unique_ptr<autofill::AutofillProvider> provider) {
DCHECK(!autofill_provider_);
@@ -995,4 +1189,16 @@ sessions::SessionTabHelperDelegate* TabImpl::GetSessionServiceTabHelperDelegate(
return browser_ ? browser_->browser_persister() : nullptr;
}
+bool TabImpl::SetDataInternal(const std::map<std::string, std::string>& data) {
+ int total_size = 0;
+ for (const auto& kv : data)
+ total_size += kv.first.size() + kv.second.size();
+ if (total_size > kMaxDataSize)
+ return false;
+ data_ = data;
+ for (auto& observer : data_observers_)
+ observer.OnDataChanged(this, data_);
+ return true;
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/tab_impl.h b/chromium/weblayer/browser/tab_impl.h
index 7d1c3949ad3..a42739b4856 100644
--- a/chromium/weblayer/browser/tab_impl.h
+++ b/chromium/weblayer/browser/tab_impl.h
@@ -6,6 +6,7 @@
#define WEBLAYER_BROWSER_TAB_IMPL_H_
#include <memory>
+#include <set>
#include "base/callback_forward.h"
#include "base/macros.h"
@@ -25,6 +26,10 @@
#include "weblayer/browser/browser_controls_navigation_state_handler_delegate.h"
#endif
+namespace js_injection {
+class JsCommunicationHost;
+}
+
namespace autofill {
class AutofillProvider;
} // namespace autofill
@@ -52,10 +57,12 @@ class FullscreenDelegate;
class NavigationControllerImpl;
class NewTabDelegate;
class ProfileImpl;
+class HttpAuthHandlerImpl;
#if defined(OS_ANDROID)
class BrowserControlsContainerView;
enum class ControlsVisibilityReason;
+class WebMessageHostFactoryProxy;
#endif
class TabImpl : public Tab,
@@ -81,6 +88,14 @@ class TabImpl : public Tab,
kBitmapAllocationFailed,
};
+ class DataObserver {
+ public:
+ // Called when SetData() is called on |tab|.
+ virtual void OnDataChanged(
+ TabImpl* tab,
+ const std::map<std::string, std::string>& data) = 0;
+ };
+
// TODO(sky): investigate a better way to not have so many ifdefs.
#if defined(OS_ANDROID)
TabImpl(ProfileImpl* profile,
@@ -95,6 +110,8 @@ class TabImpl : public Tab,
// null if |web_contents| was not created by a TabImpl.
static TabImpl* FromWebContents(content::WebContents* web_contents);
+ static std::set<TabImpl*> GetAllTabImpl();
+
ProfileImpl* profile() { return profile_; }
void set_browser(BrowserImpl* browser) { browser_ = browser; }
@@ -103,6 +120,7 @@ class TabImpl : public Tab,
content::WebContents* web_contents() const { return web_contents_.get(); }
bool has_new_tab_delegate() const { return new_tab_delegate_ != nullptr; }
+ NewTabDelegate* new_tab_delegate() const { return new_tab_delegate_; }
// Called from Browser when this Tab is losing active status.
void OnLosingActive();
@@ -111,6 +129,9 @@ class TabImpl : public Tab,
void ShowContextMenu(const content::ContextMenuParams& params);
+ void ShowHttpAuthPrompt(HttpAuthHandlerImpl* auth_handler);
+ void CloseHttpAuthPrompt();
+
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> GetJavaTab() {
return java_impl_;
@@ -143,23 +164,41 @@ class TabImpl : public Tab,
void OnAutofillProviderChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& autofill_provider);
-
void UpdateBrowserControlsState(JNIEnv* env,
jint raw_new_state,
jboolean animate);
base::android::ScopedJavaLocalRef<jstring> GetGuid(JNIEnv* env);
-
void CaptureScreenShot(
JNIEnv* env,
jfloat scale,
const base::android::JavaParamRef<jobject>& value_callback);
+ jboolean SetData(JNIEnv* env,
+ const base::android::JavaParamRef<jobjectArray>& data);
+ base::android::ScopedJavaLocalRef<jobjectArray> GetData(JNIEnv* env);
jboolean IsRendererControllingBrowserControlsOffsets(JNIEnv* env);
+ void SetHttpAuth(JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& username,
+ const base::android::JavaParamRef<jstring>& password);
+ void CancelHttpAuth(JNIEnv* env);
+ base::android::ScopedJavaLocalRef<jstring> RegisterWebMessageCallback(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& js_object_name,
+ const base::android::JavaParamRef<jobjectArray>& origins,
+ const base::android::JavaParamRef<jobject>& client);
+ void UnregisterWebMessageCallback(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jstring>& js_object_name);
+ jboolean CanTranslate(JNIEnv* env);
+ void ShowTranslateUi(JNIEnv* env);
#endif
ErrorPageDelegate* error_page_delegate() { return error_page_delegate_; }
+ void AddDataObserver(DataObserver* observer);
+ void RemoveDataObserver(DataObserver* observer);
+
// Tab:
void SetErrorPageDelegate(ErrorPageDelegate* delegate) override;
void SetFullscreenDelegate(FullscreenDelegate* delegate) override;
@@ -171,6 +210,14 @@ class TabImpl : public Tab,
bool use_separate_isolate,
JavaScriptResultCallback callback) override;
const std::string& GetGuid() override;
+ void SetData(const std::map<std::string, std::string>& data) override;
+ const std::map<std::string, std::string>& GetData() override;
+ base::string16 AddWebMessageHostFactory(
+ std::unique_ptr<WebMessageHostFactory> factory,
+ const base::string16& js_object_name,
+ const std::vector<std::string>& js_origins) override;
+ void RemoveWebMessageHostFactory(
+ const base::string16& js_object_name) override;
#if !defined(OS_ANDROID)
void AttachToView(views::WebView* web_view) override;
#endif
@@ -216,8 +263,7 @@ class TabImpl : public Tab,
const GURL& security_origin,
blink::mojom::MediaStreamType type) override;
void EnterFullscreenModeForTab(
- content::WebContents* web_contents,
- const GURL& origin,
+ content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) override;
void ExitFullscreenModeForTab(content::WebContents* web_contents) override;
bool IsFullscreenForTabOrPending(
@@ -294,6 +340,8 @@ class TabImpl : public Tab,
void UpdateBrowserVisibleSecurityStateIfNecessary();
+ bool SetDataInternal(const std::map<std::string, std::string>& data);
+
BrowserImpl* browser_ = nullptr;
ErrorPageDelegate* error_page_delegate_ = nullptr;
FullscreenDelegate* fullscreen_delegate_ = nullptr;
@@ -314,6 +362,9 @@ class TabImpl : public Tab,
// Last value supplied to UpdateBrowserControlsState().
content::BrowserControlsState current_browser_controls_state_ =
content::BROWSER_CONTROLS_STATE_SHOWN;
+
+ std::map<std::string, std::unique_ptr<WebMessageHostFactoryProxy>>
+ js_name_to_proxy_;
#endif
bool is_fullscreen_ = false;
@@ -324,8 +375,15 @@ class TabImpl : public Tab,
const std::string guid_;
+ std::map<std::string, std::string> data_;
+ base::ObserverList<DataObserver>::Unchecked data_observers_;
+
base::string16 title_;
+ HttpAuthHandlerImpl* auth_handler_ = nullptr;
+
+ std::unique_ptr<js_injection::JsCommunicationHost> js_communication_host_;
+
base::WeakPtrFactory<TabImpl> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(TabImpl);
diff --git a/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc b/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc
index 8235f633e2f..13c9c791d74 100644
--- a/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc
+++ b/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc
@@ -6,9 +6,11 @@
#include "base/bind_helpers.h"
#include "components/content_settings/core/common/content_settings.h"
+#include "components/permissions/permission_decision_auto_blocker.h"
#include "content/public/browser/render_process_host.h"
#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/host_content_settings_map_factory.h"
+#include "weblayer/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "weblayer/common/renderer_configuration.mojom.h"
namespace weblayer {
@@ -59,6 +61,15 @@ HostContentSettingsMap* TabSpecificContentSettingsDelegate::GetSettingsMap() {
web_contents_->GetBrowserContext());
}
+ContentSetting TabSpecificContentSettingsDelegate::GetEmbargoSetting(
+ const GURL& request_origin,
+ ContentSettingsType permission) {
+ return PermissionDecisionAutoBlockerFactory::GetForBrowserContext(
+ web_contents_->GetBrowserContext())
+ ->GetEmbargoResult(request_origin, permission)
+ .content_setting;
+}
+
std::vector<storage::FileSystemType>
TabSpecificContentSettingsDelegate::GetAdditionalFileSystemTypes() {
return {};
diff --git a/chromium/weblayer/browser/tab_specific_content_settings_delegate.h b/chromium/weblayer/browser/tab_specific_content_settings_delegate.h
index c33befe3189..10dcf2fe26f 100644
--- a/chromium/weblayer/browser/tab_specific_content_settings_delegate.h
+++ b/chromium/weblayer/browser/tab_specific_content_settings_delegate.h
@@ -32,6 +32,8 @@ class TabSpecificContentSettingsDelegate
const RendererContentSettingRules& rules) override;
PrefService* GetPrefs() override;
HostContentSettingsMap* GetSettingsMap() override;
+ ContentSetting GetEmbargoSetting(const GURL& request_origin,
+ ContentSettingsType permission) override;
std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() override;
browsing_data::CookieHelper::IsDeletionDisabledCallback
GetIsDeletionDisabledCallback() override;
diff --git a/chromium/weblayer/browser/translate_browsertest.cc b/chromium/weblayer/browser/translate_browsertest.cc
index 77daf3c92e4..2824d8b91ea 100644
--- a/chromium/weblayer/browser/translate_browsertest.cc
+++ b/chromium/weblayer/browser/translate_browsertest.cc
@@ -2,13 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "build/build_config.h"
#include "components/translate/content/browser/translate_waiter.h"
#include "components/translate/core/browser/language_state.h"
#include "components/translate/core/browser/translate_error_details.h"
#include "components/translate/core/browser/translate_manager.h"
#include "components/translate/core/common/translate_switches.h"
+#include "content/public/browser/browser_context.h"
+#include "net/base/mock_network_change_notifier.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/browser/translate_client_impl.h"
#include "weblayer/public/tab.h"
@@ -16,6 +21,15 @@
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "components/infobars/core/infobar_manager.h" // nogncheck
+#include "components/translate/core/browser/translate_download_manager.h"
+#include "weblayer/browser/infobar_android.h"
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/translate_compact_infobar.h"
+#endif
+
namespace weblayer {
namespace {
@@ -98,6 +112,35 @@ void WaitUntilPageTranslated(Shell* shell) {
} // namespace
+#if defined(OS_ANDROID)
+class TestInfoBarManagerObserver : public infobars::InfoBarManager::Observer {
+ public:
+ TestInfoBarManagerObserver() = default;
+ ~TestInfoBarManagerObserver() override = default;
+ void OnInfoBarAdded(infobars::InfoBar* infobar) override {
+ if (on_infobar_added_callback_)
+ std::move(on_infobar_added_callback_).Run();
+ }
+
+ void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override {
+ if (on_infobar_removed_callback_)
+ std::move(on_infobar_removed_callback_).Run();
+ }
+
+ void set_on_infobar_added_callback(base::OnceClosure callback) {
+ on_infobar_added_callback_ = std::move(callback);
+ }
+
+ void set_on_infobar_removed_callback(base::OnceClosure callback) {
+ on_infobar_removed_callback_ = std::move(callback);
+ }
+
+ private:
+ base::OnceClosure on_infobar_added_callback_;
+ base::OnceClosure on_infobar_removed_callback_;
+};
+#endif // if defined(OS_ANDROID)
+
class TranslateBrowserTest : public WebLayerBrowserTest {
public:
TranslateBrowserTest() {
@@ -112,6 +155,26 @@ class TranslateBrowserTest : public WebLayerBrowserTest {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&TranslateBrowserTest::HandleRequest, base::Unretained(this)));
embedded_test_server()->StartAcceptingConnections();
+
+ // Translation will not be offered if NetworkChangeNotifier reports that the
+ // app is offline, which can occur on bots. Prevent this.
+ // NOTE: MockNetworkChangeNotifier cannot be instantiated earlier than this
+ // due to its dependence on browser state having been created.
+ mock_network_change_notifier_ =
+ std::make_unique<net::test::ScopedMockNetworkChangeNotifier>();
+ mock_network_change_notifier_->mock_network_change_notifier()
+ ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
+
+ // By default, translation is not offered if the Google API key is not set.
+ GetTranslateClient(shell())
+ ->GetTranslateManager()
+ ->SetIgnoreMissingKeyForTesting(true);
+
+ GetTranslateClient(shell())->GetTranslatePrefs()->ResetToDefaults();
+ }
+
+ void TearDownOnMainThread() override {
+ mock_network_change_notifier_.reset();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -146,6 +209,9 @@ class TranslateBrowserTest : public WebLayerBrowserTest {
error_type_ = details.error;
}
+ std::unique_ptr<net::test::ScopedMockNetworkChangeNotifier>
+ mock_network_change_notifier_;
+
translate::TranslateErrors::Type error_type_ =
translate::TranslateErrors::NONE;
std::unique_ptr<
@@ -203,8 +269,54 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, PageTranslationSuccess) {
EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
}
+class IncognitoTranslateBrowserTest : public TranslateBrowserTest {
+ public:
+ IncognitoTranslateBrowserTest() { SetShellStartsInIncognitoMode(); }
+};
+
+// Test that the translation infrastructure is set up properly when the user is
+// in incognito mode.
+IN_PROC_BROWSER_TEST_F(IncognitoTranslateBrowserTest,
+ PageTranslationSuccess_IncognitoMode) {
+ ASSERT_TRUE(GetProfile()->GetBrowserContext()->IsOffTheRecord());
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ // Navigate to a page in French.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ // Translate the page through TranslateManager.
+ translate::TranslateManager* manager =
+ translate_client->GetTranslateManager();
+ manager->TranslatePage(
+ translate_client->GetLanguageState().original_language(), "en", true);
+
+ WaitUntilPageTranslated(shell());
+
+ EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
+ EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
+}
+
// Test if there was an error during translation.
IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, PageTranslationError) {
+#if defined(OS_ANDROID)
+ // TODO(crbug.com/1094903): Determine why this test times out on the M
+ // trybot.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() <=
+ base::android::SDK_VERSION_MARSHMALLOW) {
+ return;
+ }
+#endif
+
SetTranslateScript(kTestValidScript);
TranslateClientImpl* translate_client = GetTranslateClient(shell());
@@ -285,4 +397,380 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, PageTranslationTimeoutError) {
GetPageTranslatedResult());
}
+// Test that autotranslation kicks in if configured via prefs.
+IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, Autotranslation) {
+#if defined(OS_ANDROID)
+ // TODO(crbug.com/1094903): Determine why this test times out on the M
+ // trybot.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() <=
+ base::android::SDK_VERSION_MARSHMALLOW) {
+ return;
+ }
+#endif
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ // Before browsing, set autotranslate from French to Chinese.
+ translate_client->GetTranslatePrefs()->WhitelistLanguagePair("fr", "zh-CN");
+
+ // Navigate to a page in French.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ // Autotranslation should kick in.
+ WaitUntilPageTranslated(shell());
+
+ EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
+ EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
+ EXPECT_EQ("zh-CN", translate_client->GetLanguageState().current_language());
+}
+
+#if defined(OS_ANDROID)
+// Test that the translation infobar is presented when visiting a page with a
+// translation opportunity and removed when navigating away.
+IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslateInfoBarPresentation) {
+ auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
+ auto* infobar_service = InfoBarService::FromWebContents(web_contents);
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ TestInfoBarManagerObserver infobar_observer;
+ infobar_service->AddObserver(&infobar_observer);
+
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+ // Navigate to a page in French.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ // The translate infobar should be added.
+ run_loop.Run();
+
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+ auto* infobar = static_cast<InfoBarAndroid*>(infobar_service->infobar_at(0));
+ EXPECT_TRUE(infobar->HasSetJavaInfoBar());
+
+ base::RunLoop run_loop2;
+ infobar_observer.set_on_infobar_removed_callback(run_loop2.QuitClosure());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+
+ // The translate infobar should be removed.
+ run_loop2.Run();
+
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+ infobar_service->RemoveObserver(&infobar_observer);
+}
+#endif
+
+#if defined(OS_ANDROID)
+// Test that the translation can be successfully initiated via infobar.
+IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslationViaInfoBar) {
+ // TODO(crbug.com/1094903): Determine why this test times out on the M
+ // trybot.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() <=
+ base::android::SDK_VERSION_MARSHMALLOW) {
+ return;
+ }
+
+ auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
+ auto* infobar_service = InfoBarService::FromWebContents(web_contents);
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ TestInfoBarManagerObserver infobar_observer;
+ infobar_service->AddObserver(&infobar_observer);
+
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ // Navigate to a page in French and wait for the infobar to be added.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+
+ // Select the target language via the Java infobar and ensure that translation
+ // occurs.
+ auto* infobar =
+ static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0));
+ infobar->SelectButtonForTesting(InfoBarAndroid::ActionType::ACTION_TRANSLATE);
+
+ WaitUntilPageTranslated(shell());
+
+ EXPECT_FALSE(translate_client->GetLanguageState().translation_error());
+ EXPECT_EQ(translate::TranslateErrors::NONE, GetPageTranslatedResult());
+
+ // The translate infobar should still be present.
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ // NOTE: The notification that the translate state of the page changed can
+ // occur synchronously once reversion is initiated, so it's necessary to start
+ // listening for that notification prior to initiating the reversion.
+ auto translate_reversion_waiter = CreateTranslateWaiter(
+ shell(), translate::TranslateWaiter::WaitEvent::kIsPageTranslatedChanged);
+
+ // Revert to the source language via the Java infobar and ensure that the
+ // translation is undone.
+ infobar->SelectButtonForTesting(
+ InfoBarAndroid::ActionType::ACTION_TRANSLATE_SHOW_ORIGINAL);
+
+ translate_reversion_waiter->Wait();
+ EXPECT_EQ("fr", translate_client->GetLanguageState().current_language());
+
+ // The translate infobar should still be present.
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ infobar_service->RemoveObserver(&infobar_observer);
+}
+#endif
+
+#if defined(OS_ANDROID)
+// Test that the translation infobar stays present when the "never translate
+// language" item is clicked. Note that this behavior is intentionally different
+// from that of Chrome, where the infobar is removed in this case and a snackbar
+// is shown. As WebLayer has no snackbars, the UX decision was to simply leave
+// the infobar open to allow the user to revert the decision if desired.
+IN_PROC_BROWSER_TEST_F(TranslateBrowserTest,
+ TranslateInfoBarNeverTranslateLanguage) {
+ auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
+ auto* infobar_service = InfoBarService::FromWebContents(web_contents);
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ TestInfoBarManagerObserver infobar_observer;
+ infobar_service->AddObserver(&infobar_observer);
+
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ // Navigate to a page in French and wait for the infobar to be added.
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+
+ auto* infobar =
+ static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0));
+ infobar->ClickOverflowMenuItemForTesting(
+ TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE);
+
+ // The translate infobar should still be present.
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ // However, the infobar should not be shown on a new navigation to a page in
+ // French.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ // NOTE: There is no notification to wait for for the event of the infobar not
+ // showing. However, in practice the infobar is added synchronously, so if it
+ // were to be shown, this check would fail.
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+
+ // The infobar *should* be shown on a navigation to this site if the page's
+ // language is detected as something other than French.
+ base::RunLoop run_loop2;
+ infobar_observer.set_on_infobar_added_callback(run_loop2.QuitClosure());
+
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
+
+ run_loop2.Run();
+
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ infobar_service->RemoveObserver(&infobar_observer);
+}
+
+// Test that the translation infobar stays present when the "never translate
+// site" item is clicked. Note that this behavior is intentionally different
+// from that of Chrome, where the infobar is removed in this case and a snackbar
+// is shown. As WebLayer has no snackbars, the UX decision was to simply leave
+// the infobar open to allow the user to revert the decision if desired.
+IN_PROC_BROWSER_TEST_F(TranslateBrowserTest,
+ TranslateInfoBarNeverTranslateSite) {
+ auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
+ auto* infobar_service = InfoBarService::FromWebContents(web_contents);
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ TestInfoBarManagerObserver infobar_observer;
+ infobar_service->AddObserver(&infobar_observer);
+
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ // Navigate to a page in French and wait for the infobar to be added.
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+
+ auto* infobar =
+ static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0));
+ infobar->ClickOverflowMenuItemForTesting(
+ TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_SITE);
+
+ // The translate infobar should still be present.
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ // However, the infobar should not be shown on a new navigation to this site,
+ // independent of the detected language.
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ // NOTE: There is no notification to wait for for the event of the infobar not
+ // showing. However, in practice the infobar is added synchronously, so if it
+ // were to be shown, this check would fail.
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+
+ infobar_service->RemoveObserver(&infobar_observer);
+}
+
+// Parameterized to run tests on the "never translate language" and "never
+// translate site" menu items.
+class NeverTranslateMenuItemTranslateBrowserTest
+ : public TranslateBrowserTest,
+ public testing::WithParamInterface<
+ TranslateCompactInfoBar::OverflowMenuItemId> {};
+
+// Test that clicking and unclicking a never translate item ends up being a
+// no-op.
+IN_PROC_BROWSER_TEST_P(NeverTranslateMenuItemTranslateBrowserTest,
+ TranslateInfoBarToggleAndToggleBackNeverTranslateItem) {
+ auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
+ auto* infobar_service = InfoBarService::FromWebContents(web_contents);
+
+ SetTranslateScript(kTestValidScript);
+
+ TranslateClientImpl* translate_client = GetTranslateClient(shell());
+
+ NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("und", translate_client->GetLanguageState().original_language());
+
+ TestInfoBarManagerObserver infobar_observer;
+ infobar_service->AddObserver(&infobar_observer);
+
+ // Navigate to a page in French, wait for the infobar to be added, and click
+ // twice on the given overflow menu item.
+ {
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ EXPECT_EQ(0u, infobar_service->infobar_count());
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+
+ auto* infobar =
+ static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0));
+ infobar->ClickOverflowMenuItemForTesting(GetParam());
+
+ // The translate infobar should still be present.
+ EXPECT_EQ(1u, infobar_service->infobar_count());
+
+ infobar->ClickOverflowMenuItemForTesting(GetParam());
+ }
+
+ // The infobar should be shown on a new navigation to a page in the same
+ // language.
+ {
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/french_page2.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("fr", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+ }
+
+ // The infobar should be shown on a new navigation to a page in a different
+ // language in the same site.
+ {
+ base::RunLoop run_loop;
+ infobar_observer.set_on_infobar_added_callback(run_loop.QuitClosure());
+
+ NavigateAndWaitForCompletion(
+ GURL(embedded_test_server()->GetURL("/german_page.html")), shell());
+ WaitUntilLanguageDetermined(shell());
+ EXPECT_EQ("de", translate_client->GetLanguageState().original_language());
+
+ run_loop.Run();
+ }
+
+ infobar_service->RemoveObserver(&infobar_observer);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ NeverTranslateMenuItemTranslateBrowserTest,
+ ::testing::Values(
+ TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE,
+ TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_SITE));
+
+#endif // #if defined(OS_ANDROID)
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/translate_client_impl.cc b/chromium/weblayer/browser/translate_client_impl.cc
index 8166e11b1ab..dbe11d68bbc 100644
--- a/chromium/weblayer/browser/translate_client_impl.cc
+++ b/chromium/weblayer/browser/translate_client_impl.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
+#include "build/build_config.h"
#include "components/infobars/core/infobar.h"
#include "components/language/core/browser/pref_names.h"
#include "components/translate/content/browser/content_translate_driver.h"
@@ -21,6 +22,11 @@
#include "weblayer/browser/translate_accept_languages_factory.h"
#include "weblayer/browser/translate_ranker_factory.h"
+#if defined(OS_ANDROID)
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/translate_compact_infobar.h"
+#endif
+
namespace weblayer {
namespace {
@@ -54,6 +60,7 @@ TranslateClientImpl::TranslateClientImpl(content::WebContents* web_contents)
TranslateRankerFactory::GetForBrowserContext(
web_contents->GetBrowserContext()),
/*language_model=*/nullptr)) {
+ observer_.Add(&translate_driver_);
translate_driver_.set_translate_manager(translate_manager_.get());
}
@@ -69,6 +76,18 @@ bool TranslateClientImpl::ShowTranslateUI(
const std::string& target_language,
translate::TranslateErrors::Type error_type,
bool triggered_from_menu) {
+ if (error_type != translate::TranslateErrors::NONE)
+ step = translate::TRANSLATE_STEP_TRANSLATE_ERROR;
+
+#if defined(OS_ANDROID)
+ translate::TranslateInfoBarDelegate::Create(
+ step != translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
+ translate_manager_->GetWeakPtr(),
+ InfoBarService::FromWebContents(web_contents()),
+ web_contents()->GetBrowserContext()->IsOffTheRecord(), step,
+ source_language, target_language, error_type, triggered_from_menu);
+ return true;
+#endif
return false;
}
@@ -100,8 +119,7 @@ TranslateClientImpl::GetTranslateAcceptLanguages() {
#if defined(OS_ANDROID)
std::unique_ptr<infobars::InfoBar> TranslateClientImpl::CreateInfoBar(
std::unique_ptr<translate::TranslateInfoBarDelegate> delegate) const {
- NOTREACHED();
- return nullptr;
+ return std::make_unique<TranslateCompactInfoBar>(std::move(delegate));
}
int TranslateClientImpl::GetInfobarIconID() const {
@@ -119,6 +137,22 @@ void TranslateClientImpl::ShowReportLanguageDetectionErrorUI(
NOTREACHED();
}
+void TranslateClientImpl::OnLanguageDetermined(
+ const translate::LanguageDetectionDetails& details) {
+ if (manual_translate_on_ready_) {
+ GetTranslateManager()->InitiateManualTranslation();
+ manual_translate_on_ready_ = false;
+ }
+}
+
+void TranslateClientImpl::ManualTranslateWhenReady() {
+ if (GetLanguageState().original_language().empty()) {
+ manual_translate_on_ready_ = true;
+ } else {
+ GetTranslateManager()->InitiateManualTranslation();
+ }
+}
+
void TranslateClientImpl::WebContentsDestroyed() {
// Translation process can be interrupted.
// Destroying the TranslateManager now guarantees that it never has to deal
diff --git a/chromium/weblayer/browser/translate_client_impl.h b/chromium/weblayer/browser/translate_client_impl.h
index 760d69dbfaf..c33e77eb77b 100644
--- a/chromium/weblayer/browser/translate_client_impl.h
+++ b/chromium/weblayer/browser/translate_client_impl.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "base/scoped_observer.h"
#include "build/build_config.h"
#include "components/translate/content/browser/content_translate_driver.h"
#include "components/translate/core/browser/translate_client.h"
@@ -27,6 +28,7 @@ namespace weblayer {
class TranslateClientImpl
: public translate::TranslateClient,
+ public translate::ContentTranslateDriver::Observer,
public content::WebContentsObserver,
public content::WebContentsUserData<TranslateClientImpl> {
public:
@@ -63,6 +65,14 @@ class TranslateClientImpl
bool IsTranslatableURL(const GURL& url) override;
void ShowReportLanguageDetectionErrorUI(const GURL& report_url) override;
+ // ContentTranslateDriver::Observer implementation.
+ void OnLanguageDetermined(
+ const translate::LanguageDetectionDetails& details) override;
+
+ // Trigger a manual translation when the necessary state (e.g. source
+ // language) is ready.
+ void ManualTranslateWhenReady();
+
private:
explicit TranslateClientImpl(content::WebContents* web_contents);
friend class content::WebContentsUserData<TranslateClientImpl>;
@@ -73,6 +83,13 @@ class TranslateClientImpl
translate::ContentTranslateDriver translate_driver_;
std::unique_ptr<translate::TranslateManager> translate_manager_;
+ // Whether to trigger a manual translation when ready.
+ bool manual_translate_on_ready_ = false;
+
+ ScopedObserver<translate::ContentTranslateDriver,
+ translate::ContentTranslateDriver::Observer>
+ observer_{this};
+
WEB_CONTENTS_USER_DATA_KEY_DECL();
DISALLOW_COPY_AND_ASSIGN(TranslateClientImpl);
diff --git a/chromium/weblayer/browser/translate_compact_infobar.cc b/chromium/weblayer/browser/translate_compact_infobar.cc
new file mode 100644
index 00000000000..7584fe4e99a
--- /dev/null
+++ b/chromium/weblayer/browser/translate_compact_infobar.cc
@@ -0,0 +1,246 @@
+// 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.
+
+#include "weblayer/browser/translate_compact_infobar.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/jni_weak_ref.h"
+#include "components/translate/core/browser/translate_infobar_delegate.h"
+#include "components/variations/variations_associated_data.h"
+#include "content/public/browser/browser_context.h"
+#include "weblayer/browser/infobar_service.h"
+#include "weblayer/browser/java/jni/TranslateCompactInfoBar_jni.h"
+#include "weblayer/browser/tab_impl.h"
+#include "weblayer/browser/translate_client_impl.h"
+#include "weblayer/browser/translate_utils.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace weblayer {
+
+// Finch parameter names:
+const char kTranslateTabDefaultTextColor[] = "translate_tab_default_text_color";
+
+// TranslateInfoBar -----------------------------------------------------------
+
+TranslateCompactInfoBar::TranslateCompactInfoBar(
+ std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
+ : InfoBarAndroid(std::move(delegate)), action_flags_(FLAG_NONE) {
+ GetDelegate()->AddObserver(this);
+
+ // Flip the translate bit if auto translate is enabled.
+ if (GetDelegate()->translate_step() == translate::TRANSLATE_STEP_TRANSLATING)
+ action_flags_ |= FLAG_TRANSLATE;
+}
+
+TranslateCompactInfoBar::~TranslateCompactInfoBar() {
+ GetDelegate()->RemoveObserver(this);
+}
+
+ScopedJavaLocalRef<jobject> TranslateCompactInfoBar::CreateRenderInfoBar(
+ JNIEnv* env) {
+ translate::TranslateInfoBarDelegate* delegate = GetDelegate();
+
+ base::android::ScopedJavaLocalRef<jobjectArray> java_languages =
+ TranslateUtils::GetJavaLanguages(env, delegate);
+ base::android::ScopedJavaLocalRef<jobjectArray> java_codes =
+ TranslateUtils::GetJavaLanguageCodes(env, delegate);
+ base::android::ScopedJavaLocalRef<jintArray> java_hash_codes =
+ TranslateUtils::GetJavaLanguageHashCodes(env, delegate);
+
+ ScopedJavaLocalRef<jstring> source_language_code =
+ base::android::ConvertUTF8ToJavaString(
+ env, delegate->original_language_code());
+
+ ScopedJavaLocalRef<jstring> target_language_code =
+ base::android::ConvertUTF8ToJavaString(env,
+ delegate->target_language_code());
+ content::WebContents* web_contents =
+ InfoBarService::WebContentsFromInfoBar(this);
+
+ TabImpl* tab =
+ web_contents ? TabImpl::FromWebContents(web_contents) : nullptr;
+
+ return Java_TranslateCompactInfoBar_create(
+ env, tab ? tab->GetJavaTab() : nullptr, delegate->translate_step(),
+ source_language_code, target_language_code,
+ delegate->ShouldAlwaysTranslate(), delegate->triggered_from_menu(),
+ java_languages, java_codes, java_hash_codes, TabDefaultTextColor());
+}
+
+void TranslateCompactInfoBar::ProcessButton(int action) {
+ if (!owner())
+ return; // We're closing; don't call anything, it might access the owner.
+
+ translate::TranslateInfoBarDelegate* delegate = GetDelegate();
+ if (action == InfoBarAndroid::ACTION_TRANSLATE) {
+ action_flags_ |= FLAG_TRANSLATE;
+ delegate->Translate();
+ if (delegate->ShouldAutoAlwaysTranslate()) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_TranslateCompactInfoBar_setAutoAlwaysTranslate(env,
+ GetJavaInfoBar());
+ }
+ } else if (action == InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) {
+ action_flags_ |= FLAG_REVERT;
+ delegate->RevertWithoutClosingInfobar();
+ } else {
+ DCHECK_EQ(InfoBarAndroid::ACTION_NONE, action);
+ }
+}
+
+void TranslateCompactInfoBar::SetJavaInfoBar(
+ const base::android::JavaRef<jobject>& java_info_bar) {
+ InfoBarAndroid::SetJavaInfoBar(java_info_bar);
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_TranslateCompactInfoBar_setNativePtr(env, java_info_bar,
+ reinterpret_cast<intptr_t>(this));
+}
+
+void TranslateCompactInfoBar::ApplyStringTranslateOption(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ int option,
+ const JavaParamRef<jstring>& value) {
+ translate::TranslateInfoBarDelegate* delegate = GetDelegate();
+ if (option == TranslateUtils::OPTION_SOURCE_CODE) {
+ std::string source_code =
+ base::android::ConvertJavaStringToUTF8(env, value);
+ if (delegate->original_language_code().compare(source_code) != 0)
+ delegate->UpdateOriginalLanguage(source_code);
+ } else if (option == TranslateUtils::OPTION_TARGET_CODE) {
+ std::string target_code =
+ base::android::ConvertJavaStringToUTF8(env, value);
+ if (delegate->target_language_code().compare(target_code) != 0)
+ delegate->UpdateTargetLanguage(target_code);
+ } else {
+ DCHECK(false);
+ }
+}
+
+void TranslateCompactInfoBar::ApplyBoolTranslateOption(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ int option,
+ jboolean value) {
+ translate::TranslateInfoBarDelegate* delegate = GetDelegate();
+ if (option == TranslateUtils::OPTION_ALWAYS_TRANSLATE) {
+ if (delegate->ShouldAlwaysTranslate() != value) {
+ action_flags_ |= FLAG_ALWAYS_TRANSLATE;
+ delegate->ToggleAlwaysTranslate();
+ }
+ } else if (option == TranslateUtils::OPTION_NEVER_TRANSLATE) {
+ bool language_blocklisted = !delegate->IsTranslatableLanguageByPrefs();
+ if (language_blocklisted != value) {
+ action_flags_ |= FLAG_NEVER_LANGUAGE;
+ delegate->ToggleTranslatableLanguageByPrefs();
+ }
+ } else if (option == TranslateUtils::OPTION_NEVER_TRANSLATE_SITE) {
+ if (delegate->IsSiteBlacklisted() != value) {
+ action_flags_ |= FLAG_NEVER_SITE;
+ delegate->ToggleSiteBlacklist();
+ }
+ } else {
+ DCHECK(false);
+ }
+}
+
+jboolean TranslateCompactInfoBar::ShouldAutoNeverTranslate(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jboolean menu_expanded) {
+ // Flip menu expanded bit.
+ if (menu_expanded)
+ action_flags_ |= FLAG_EXPAND_MENU;
+
+ if (!IsDeclinedByUser())
+ return false;
+
+ return GetDelegate()->ShouldAutoNeverTranslate();
+}
+
+// Returns true if the current tab is an incognito tab.
+jboolean TranslateCompactInfoBar::IsIncognito(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj) {
+ content::WebContents* web_contents =
+ InfoBarService::WebContentsFromInfoBar(this);
+ if (!web_contents)
+ return false;
+ return web_contents->GetBrowserContext()->IsOffTheRecord();
+}
+
+int TranslateCompactInfoBar::GetParam(const std::string& paramName,
+ int default_value) {
+ std::map<std::string, std::string> params;
+ if (!variations::GetVariationParams(translate::kTranslateCompactUI.name,
+ &params))
+ return default_value;
+ int value = 0;
+ base::StringToInt(params[paramName], &value);
+ return value <= 0 ? default_value : value;
+}
+
+int TranslateCompactInfoBar::TabDefaultTextColor() {
+ return GetParam(kTranslateTabDefaultTextColor, 0);
+}
+
+translate::TranslateInfoBarDelegate* TranslateCompactInfoBar::GetDelegate() {
+ return delegate()->AsTranslateInfoBarDelegate();
+}
+
+void TranslateCompactInfoBar::OnTranslateStepChanged(
+ translate::TranslateStep step,
+ translate::TranslateErrors::Type error_type) {
+ if (!owner())
+ return; // We're closing; don't call anything.
+
+ if ((step == translate::TRANSLATE_STEP_AFTER_TRANSLATE) ||
+ (step == translate::TRANSLATE_STEP_TRANSLATE_ERROR)) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_TranslateCompactInfoBar_onPageTranslated(env, GetJavaInfoBar(),
+ error_type);
+ }
+}
+
+bool TranslateCompactInfoBar::IsDeclinedByUser() {
+ // Whether there is any affirmative action bit.
+ return action_flags_ == FLAG_NONE;
+}
+
+void TranslateCompactInfoBar::OnTranslateInfoBarDelegateDestroyed(
+ translate::TranslateInfoBarDelegate* delegate) {
+ DCHECK_EQ(GetDelegate(), delegate);
+ GetDelegate()->RemoveObserver(this);
+}
+
+void TranslateCompactInfoBar::SelectButtonForTesting(ActionType action_type) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_TranslateCompactInfoBar_selectTabForTesting(env, GetJavaInfoBar(),
+ action_type);
+}
+
+void TranslateCompactInfoBar::ClickOverflowMenuItemForTesting(
+ OverflowMenuItemId item_id) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ switch (item_id) {
+ case OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE:
+ Java_TranslateCompactInfoBar_clickNeverTranslateLanguageMenuItemForTesting(
+ env, GetJavaInfoBar());
+ return;
+ case OverflowMenuItemId::NEVER_TRANSLATE_SITE:
+ Java_TranslateCompactInfoBar_clickNeverTranslateSiteMenuItemForTesting(
+ env, GetJavaInfoBar());
+ return;
+ }
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/translate_compact_infobar.h b/chromium/weblayer/browser/translate_compact_infobar.h
new file mode 100644
index 00000000000..d4cfc20c3fd
--- /dev/null
+++ b/chromium/weblayer/browser/translate_compact_infobar.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_TRANSLATE_COMPACT_INFOBAR_H_
+#define WEBLAYER_BROWSER_TRANSLATE_COMPACT_INFOBAR_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "components/translate/core/browser/translate_infobar_delegate.h"
+#include "components/translate/core/browser/translate_step.h"
+#include "components/translate/core/common/translate_errors.h"
+#include "weblayer/browser/infobar_android.h"
+
+namespace translate {
+class TranslateInfoBarDelegate;
+}
+
+namespace weblayer {
+
+class TranslateCompactInfoBar
+ : public InfoBarAndroid,
+ public translate::TranslateInfoBarDelegate::Observer {
+ public:
+ explicit TranslateCompactInfoBar(
+ std::unique_ptr<translate::TranslateInfoBarDelegate> delegate);
+ ~TranslateCompactInfoBar() override;
+
+ enum class OverflowMenuItemId {
+ NEVER_TRANSLATE_LANGUAGE = 0,
+ NEVER_TRANSLATE_SITE = 1,
+ };
+
+ // JNI method specific to string settings in translate.
+ void ApplyStringTranslateOption(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ int option,
+ const base::android::JavaParamRef<jstring>& value);
+
+ // JNI method specific to boolean settings in translate.
+ void ApplyBoolTranslateOption(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ int option,
+ jboolean value);
+
+ // Check whether we should automatically trigger "Never Translate Language".
+ jboolean ShouldAutoNeverTranslate(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jboolean menu_expanded);
+
+ // Returns true if the current tab is an incognito tab.
+ jboolean IsIncognito(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+
+ // TranslateInfoBarDelegate::Observer implementation.
+ void OnTranslateStepChanged(
+ translate::TranslateStep step,
+ translate::TranslateErrors::Type error_type) override;
+ // Returns true if the user didn't take any affirmative action.
+ // The function will be called when the translate infobar is dismissed.
+ // If it's true, we will record a declined event.
+ bool IsDeclinedByUser() override;
+ void OnTranslateInfoBarDelegateDestroyed(
+ translate::TranslateInfoBarDelegate* delegate) override;
+
+ // Instructs the Java infobar to select the button corresponding to
+ // |action_type|.
+ void SelectButtonForTesting(ActionType action_type);
+
+ // Instructs the Java infobar to click the specified overflow menu item.
+ void ClickOverflowMenuItemForTesting(OverflowMenuItemId item_id);
+
+ private:
+ // InfoBarAndroid:
+ base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+ JNIEnv* env) override;
+ void ProcessButton(int action) override;
+ void SetJavaInfoBar(
+ const base::android::JavaRef<jobject>& java_info_bar) override;
+
+ // Get the value of a specified finch parameter in TranslateCompactUI. If the
+ // finch parameter does not exist, default_value will be returned.
+ int GetParam(const std::string& paramName, int default_value);
+ // Get the value of the finch parameter: translate_tab_default_text_color.
+ // Default value is 0, which means using TabLayout default color.
+ // If it's not 0, we will set the text color manually based on the value.
+ int TabDefaultTextColor();
+
+ translate::TranslateInfoBarDelegate* GetDelegate();
+
+ // Bits for trace user's affirmative actions.
+ unsigned int action_flags_;
+
+ // Affirmative action flags to record what the user has done in one session.
+ enum ActionFlag {
+ FLAG_NONE = 0,
+ FLAG_TRANSLATE = 1 << 0,
+ FLAG_REVERT = 1 << 1,
+ FLAG_ALWAYS_TRANSLATE = 1 << 2,
+ FLAG_NEVER_LANGUAGE = 1 << 3,
+ FLAG_NEVER_SITE = 1 << 4,
+ FLAG_EXPAND_MENU = 1 << 5,
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(TranslateCompactInfoBar);
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_TRANSLATE_COMPACT_INFOBAR_H_
diff --git a/chromium/weblayer/browser/translate_ranker_factory.cc b/chromium/weblayer/browser/translate_ranker_factory.cc
index 90522a17ed3..1c24edac45c 100644
--- a/chromium/weblayer/browser/translate_ranker_factory.cc
+++ b/chromium/weblayer/browser/translate_ranker_factory.cc
@@ -38,4 +38,9 @@ KeyedService* TranslateRankerFactory::BuildServiceInstanceFor(
translate::TranslateRankerImpl::GetModelURL(), ukm::UkmRecorder::Get());
}
+content::BrowserContext* TranslateRankerFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return context;
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/browser/translate_ranker_factory.h b/chromium/weblayer/browser/translate_ranker_factory.h
index 228f0db5db6..91a6e48cb0b 100644
--- a/chromium/weblayer/browser/translate_ranker_factory.h
+++ b/chromium/weblayer/browser/translate_ranker_factory.h
@@ -20,12 +20,6 @@ namespace weblayer {
// TranslateRankerFactory is a way to associate a TranslateRanker instance to a
// BrowserContext.
-// TODO(crbug.com/1072334): In //chrome, when the service is requested for a
-// Profile in incognito mode the factory supplies the associated original
-// Profile. However, WebLayer doesn't have a concept of incognito profiles being
-// associated with regular profiles. For now, we just stay with
-// GetBrowserContextToUse()'s default behavior of returning nullptr in this case
-// pending resolution of this question.
class TranslateRankerFactory : public BrowserContextKeyedServiceFactory {
public:
TranslateRankerFactory(const TranslateRankerFactory&) = delete;
@@ -44,6 +38,14 @@ class TranslateRankerFactory : public BrowserContextKeyedServiceFactory {
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
+
+ // Note: In //chrome, when the service is requested for a
+ // Profile in incognito mode the factory supplies the associated original
+ // Profile. However, WebLayer doesn't have a concept of incognito profiles
+ // being associated with regular profiles, so the service gets its own
+ // instance in incognito mode.
+ content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const override;
};
} // namespace weblayer
diff --git a/chromium/weblayer/browser/translate_utils.cc b/chromium/weblayer/browser/translate_utils.cc
new file mode 100644
index 00000000000..aac744a115e
--- /dev/null
+++ b/chromium/weblayer/browser/translate_utils.cc
@@ -0,0 +1,54 @@
+// 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.
+
+#include "weblayer/browser/translate_utils.h"
+
+#include <stddef.h>
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/jni_weak_ref.h"
+#include "components/metrics/metrics_log.h"
+#include "components/translate/core/browser/translate_infobar_delegate.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace weblayer {
+
+ScopedJavaLocalRef<jobjectArray> TranslateUtils::GetJavaLanguages(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate) {
+ std::vector<base::string16> languages;
+ languages.reserve(delegate->num_languages());
+ for (size_t i = 0; i < delegate->num_languages(); ++i) {
+ languages.push_back(delegate->language_name_at(i));
+ }
+ return base::android::ToJavaArrayOfStrings(env, languages);
+}
+
+ScopedJavaLocalRef<jobjectArray> TranslateUtils::GetJavaLanguageCodes(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate) {
+ std::vector<std::string> codes;
+ codes.reserve(delegate->num_languages());
+ for (size_t i = 0; i < delegate->num_languages(); ++i) {
+ codes.push_back(delegate->language_code_at(i));
+ }
+ return base::android::ToJavaArrayOfStrings(env, codes);
+}
+
+ScopedJavaLocalRef<jintArray> TranslateUtils::GetJavaLanguageHashCodes(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate) {
+ std::vector<int> hashCodes;
+ hashCodes.reserve(delegate->num_languages());
+ for (size_t i = 0; i < delegate->num_languages(); ++i) {
+ hashCodes.push_back(
+ metrics::MetricsLog::Hash(delegate->language_code_at(i)));
+ }
+ return base::android::ToJavaIntArray(env, hashCodes);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/translate_utils.h b/chromium/weblayer/browser/translate_utils.h
new file mode 100644
index 00000000000..9bee56b5d7a
--- /dev/null
+++ b/chromium/weblayer/browser/translate_utils.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_TRANSLATE_UTILS_H_
+#define WEBLAYER_BROWSER_TRANSLATE_UTILS_H_
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+
+namespace translate {
+class TranslateInfoBarDelegate;
+}
+
+namespace weblayer {
+
+class TranslateUtils {
+ public:
+ // A Java counterpart will be generated for this enum.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private
+ // GENERATED_JAVA_PREFIX_TO_STRIP:OPTION_
+ enum TranslateOption {
+ OPTION_SOURCE_CODE,
+ OPTION_TARGET_CODE,
+ OPTION_ALWAYS_TRANSLATE,
+ OPTION_NEVER_TRANSLATE,
+ OPTION_NEVER_TRANSLATE_SITE
+ };
+
+ static base::android::ScopedJavaLocalRef<jobjectArray> GetJavaLanguages(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate);
+ static base::android::ScopedJavaLocalRef<jobjectArray> GetJavaLanguageCodes(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate);
+ static base::android::ScopedJavaLocalRef<jintArray> GetJavaLanguageHashCodes(
+ JNIEnv* env,
+ translate::TranslateInfoBarDelegate* delegate);
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_TRANSLATE_UTILS_H_
diff --git a/chromium/weblayer/browser/ukm_browsertest.cc b/chromium/weblayer/browser/ukm_browsertest.cc
new file mode 100644
index 00000000000..52b734a4559
--- /dev/null
+++ b/chromium/weblayer/browser/ukm_browsertest.cc
@@ -0,0 +1,158 @@
+// 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.
+
+#include "base/test/bind_test_util.h"
+#include "components/ukm/ukm_test_helper.h"
+#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/public/navigation_controller.h"
+#include "weblayer/public/tab.h"
+#include "weblayer/shell/android/browsertests_apk/metrics_test_helper.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/weblayer_browser_test.h"
+
+namespace weblayer {
+
+namespace {
+
+ProfileImpl* GetProfileByName(const std::string& name) {
+ for (auto* profile : ProfileImpl::GetAllProfiles()) {
+ if (profile->name() == name)
+ return profile;
+ }
+
+ return nullptr;
+}
+
+} // namespace
+
+class UkmBrowserTest : public WebLayerBrowserTest {
+ public:
+ void SetUp() override {
+ InstallTestGmsBridge(user_consent_);
+
+ WebLayerBrowserTest::SetUp();
+ }
+
+ void TearDown() override {
+ RemoveTestGmsBridge();
+ WebLayerBrowserTest::TearDown();
+ }
+
+ ukm::UkmService* GetUkmService() {
+ return WebLayerMetricsServiceClient::GetInstance()->GetUkmService();
+ }
+
+ void disable_user_consent() { user_consent_ = false; }
+
+ private:
+ bool user_consent_ = true;
+};
+
+// Even if there's user consent for UMA, need to explicitly enable UKM.
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, UserConsentButNotEnabled) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// UKMs are only enabled when there's user consent and it's explicitly enabled.
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, Enabled) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// If UKMs are disabled it's reflected accordingly.
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, EnabledThenDisable) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, false);
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// Make sure that UKM is disabled while an incognito profile is alive.
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, RegularPlusIncognitoCheck) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+ uint64_t original_client_id = ukm_test_helper.GetClientId();
+ EXPECT_NE(0U, original_client_id);
+
+ // Incognito profiles have an empty name.
+ CreateProfile(std::string());
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ // Creating another regular profile mustn't enable UKM.
+ CreateProfile("foo");
+ GetProfileByName("foo")->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ // Note WebLayer can only have one incognito profile so we can't test creating
+ // and destroying another one here.
+
+ DestroyProfile("foo");
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ DestroyProfile(std::string());
+ EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+ // Client ID should not have been reset.
+ EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());
+}
+
+// Make sure creating a real profile after Incognito doesn't enable UKM.
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, IncognitoPlusRegularCheck) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+
+ // Incognito profiles have an empty name.
+ CreateProfile(std::string());
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ CreateProfile("foo");
+ GetProfileByName("foo")->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ DestroyProfile(std::string());
+ EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+class UkmDisabledBrowserTest : public UkmBrowserTest {
+ public:
+ UkmDisabledBrowserTest() { disable_user_consent(); }
+};
+
+// If there's no user consent UKMs are disabled.
+IN_PROC_BROWSER_TEST_F(UkmDisabledBrowserTest, Disabled) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+
+ // Ensure enabling UKMs still doesn't enable it.
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+class UkmIncognitoBrowserTest : public UkmBrowserTest {
+ public:
+ UkmIncognitoBrowserTest() { SetShellStartsInIncognitoMode(); }
+};
+
+// Starting with one incognito window should disable UKM.
+IN_PROC_BROWSER_TEST_F(UkmIncognitoBrowserTest, Disabled) {
+ ukm::UkmTestHelper ukm_test_helper(GetUkmService());
+
+ // Enabling UKMs doesn't enable it because of the incognito window.
+ GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true);
+
+ EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/url_bar/url_bar_browsertest.cc b/chromium/weblayer/browser/url_bar/url_bar_browsertest.cc
index 76c39e11cd6..22dbd2092c3 100644
--- a/chromium/weblayer/browser/url_bar/url_bar_browsertest.cc
+++ b/chromium/weblayer/browser/url_bar/url_bar_browsertest.cc
@@ -8,6 +8,7 @@
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
+#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
@@ -22,16 +23,13 @@ class UrlBarBrowserTest : public WebLayerBrowserTest {
void SetUpOnMainThread() override {
WebLayerBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
- browser_ = Browser::Create(GetProfile(), nullptr);
- tab_ = static_cast<TabImpl*>(browser_->AddTab(Tab::Create(GetProfile())));
- another_tab_ =
- static_cast<TabImpl*>(browser_->AddTab(Tab::Create(GetProfile())));
- browser_->SetActiveTab(tab_);
+ tab_ = static_cast<TabImpl*>(shell()->browser()->CreateTab());
+ another_tab_ = static_cast<TabImpl*>(shell()->browser()->CreateTab());
+ SetActiveTab(tab_);
}
void PostRunTestOnMainThread() override {
tab_ = nullptr;
another_tab_ = nullptr;
- browser_.reset();
WebLayerBrowserTest::PostRunTestOnMainThread();
}
@@ -45,16 +43,15 @@ class UrlBarBrowserTest : public WebLayerBrowserTest {
std::move(closure));
}
- void SetActiveTab(TabImpl* tab) { browser_->SetActiveTab(tab); }
+ void SetActiveTab(TabImpl* tab) { shell()->browser()->SetActiveTab(tab); }
protected:
TabImpl* tab_ = nullptr;
TabImpl* another_tab_ = nullptr;
private:
- std::unique_ptr<Browser> browser_;
BrowserImpl* browser_impl() {
- return static_cast<BrowserImpl*>(browser_.get());
+ return static_cast<BrowserImpl*>(shell()->browser());
}
};
diff --git a/chromium/weblayer/browser/verdict_cache_manager_factory.cc b/chromium/weblayer/browser/verdict_cache_manager_factory.cc
new file mode 100644
index 00000000000..24ecb137b74
--- /dev/null
+++ b/chromium/weblayer/browser/verdict_cache_manager_factory.cc
@@ -0,0 +1,42 @@
+// 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.
+
+#include "weblayer/browser/verdict_cache_manager_factory.h"
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "weblayer/browser/host_content_settings_map_factory.h"
+
+namespace weblayer {
+
+// static
+safe_browsing::VerdictCacheManager*
+VerdictCacheManagerFactory::GetForBrowserContext(
+ content::BrowserContext* browser_context) {
+ return static_cast<safe_browsing::VerdictCacheManager*>(
+ GetInstance()->GetServiceForBrowserContext(browser_context,
+ /* create= */ true));
+}
+
+// static
+VerdictCacheManagerFactory* VerdictCacheManagerFactory::GetInstance() {
+ return base::Singleton<VerdictCacheManagerFactory>::get();
+}
+
+VerdictCacheManagerFactory::VerdictCacheManagerFactory()
+ : BrowserContextKeyedServiceFactory(
+ "VerdictCacheManager",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(HostContentSettingsMapFactory::GetInstance());
+}
+
+KeyedService* VerdictCacheManagerFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ return new safe_browsing::VerdictCacheManager(
+ nullptr /* history service */,
+ HostContentSettingsMapFactory::GetForBrowserContext(context));
+}
+
+} // namespace weblayer \ No newline at end of file
diff --git a/chromium/weblayer/browser/verdict_cache_manager_factory.h b/chromium/weblayer/browser/verdict_cache_manager_factory.h
new file mode 100644
index 00000000000..038347f8787
--- /dev/null
+++ b/chromium/weblayer/browser/verdict_cache_manager_factory.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef WEBLAYER_BROWSER_VERDICT_CACHE_MANAGER_FACTORY_H_
+#define WEBLAYER_BROWSER_VERDICT_CACHE_MANAGER_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class KeyedService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace safe_browsing {
+class VerdictCacheManager;
+}
+
+namespace weblayer {
+
+// Singleton that owns VerdictCacheManager objects and associates them
+// them with BrowserContextImpl instances.
+class VerdictCacheManagerFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ // Creates the manager if it doesn't exist already for the given
+ // |browser_context|. If the manager already exists, return its pointer.
+ static safe_browsing::VerdictCacheManager* GetForBrowserContext(
+ content::BrowserContext* browser_context);
+
+ // Get the singleton instance.
+ static VerdictCacheManagerFactory* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<VerdictCacheManagerFactory>;
+
+ VerdictCacheManagerFactory();
+ ~VerdictCacheManagerFactory() override = default;
+ VerdictCacheManagerFactory(const VerdictCacheManagerFactory&) = delete;
+ VerdictCacheManagerFactory& operator=(const VerdictCacheManagerFactory&) =
+ delete;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_VERDICT_CACHE_MANAGER_FACTORY_H_ \ No newline at end of file
diff --git a/chromium/weblayer/browser/weblayer_browser_interface_binders.cc b/chromium/weblayer/browser/weblayer_browser_interface_binders.cc
index a9e8fa6b606..a0043214a7e 100644
--- a/chromium/weblayer/browser/weblayer_browser_interface_binders.cc
+++ b/chromium/weblayer/browser/weblayer_browser_interface_binders.cc
@@ -19,6 +19,8 @@
#if defined(OS_ANDROID)
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
#endif
namespace weblayer {
@@ -37,11 +39,6 @@ void BindContentTranslateDriver(
if (!contents)
return;
- // TODO(crbug.com/1072334): Resolve incorporation of translate in incognito
- // mode.
- if (contents->GetBrowserContext()->IsOffTheRecord())
- return;
-
TranslateClientImpl* const translate_client =
TranslateClientImpl::FromWebContents(contents);
translate_client->translate_driver()->AddReceiver(std::move(receiver));
@@ -90,6 +87,15 @@ class StubInstalledAppProvider : public blink::mojom::InstalledAppProvider {
std::move(receiver));
}
};
+
+template <typename Interface>
+void ForwardToJavaWebContents(content::RenderFrameHost* frame_host,
+ mojo::PendingReceiver<Interface> receiver) {
+ content::WebContents* contents =
+ content::WebContents::FromRenderFrameHost(frame_host);
+ if (contents)
+ contents->GetJavaInterfaces()->GetInterface(std::move(receiver));
+}
#endif
} // namespace
@@ -107,6 +113,8 @@ void PopulateWebLayerFrameBinders(
// TODO(https://crbug.com/1037884): Remove this.
map->Add<blink::mojom::InstalledAppProvider>(
base::BindRepeating(&StubInstalledAppProvider::Create));
+ map->Add<blink::mojom::ShareService>(base::BindRepeating(
+ &ForwardToJavaWebContents<blink::mojom::ShareService>));
#endif
}
diff --git a/chromium/weblayer/browser/weblayer_features.cc b/chromium/weblayer/browser/weblayer_features.cc
new file mode 100644
index 00000000000..b6cce79dc3f
--- /dev/null
+++ b/chromium/weblayer/browser/weblayer_features.cc
@@ -0,0 +1,15 @@
+// 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.
+
+#include "weblayer/browser/weblayer_features.h"
+
+namespace weblayer {
+
+#if defined(OS_ANDROID)
+// Used to disable browser-control animations.
+const base::Feature kImmediatelyHideBrowserControlsForTest{
+ "ImmediatelyHideBrowserControlsForTest", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/weblayer_features.h b/chromium/weblayer/browser/weblayer_features.h
new file mode 100644
index 00000000000..40254557d4b
--- /dev/null
+++ b/chromium/weblayer/browser/weblayer_features.h
@@ -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.
+
+#ifndef WEBLAYER_BROWSER_WEBLAYER_FEATURES_H_
+#define WEBLAYER_BROWSER_WEBLAYER_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+
+namespace weblayer {
+
+#if defined(OS_ANDROID)
+extern const base::Feature kImmediatelyHideBrowserControlsForTest;
+#endif
+
+} // namespace weblayer
+
+#endif // WEBLAYER_BROWSER_WEBLAYER_FEATURES_H_
diff --git a/chromium/weblayer/browser/weblayer_field_trials.cc b/chromium/weblayer/browser/weblayer_field_trials.cc
new file mode 100644
index 00000000000..8ba011f7f09
--- /dev/null
+++ b/chromium/weblayer/browser/weblayer_field_trials.cc
@@ -0,0 +1,21 @@
+// 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.
+
+#include "weblayer/browser/weblayer_field_trials.h"
+
+#include "base/path_service.h"
+#include "components/metrics/persistent_histograms.h"
+#include "weblayer/common/weblayer_paths.h"
+
+namespace weblayer {
+
+void WebLayerFieldTrials::SetupFieldTrials() {
+ // Persistent histograms must be enabled as soon as possible.
+ base::FilePath metrics_dir;
+ if (base::PathService::Get(DIR_USER_DATA, &metrics_dir)) {
+ InstantiatePersistentHistograms(metrics_dir);
+ }
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/browser/weblayer_field_trials.h b/chromium/weblayer/browser/weblayer_field_trials.h
index 54454c3ba89..a2fc67cfb89 100644
--- a/chromium/weblayer/browser/weblayer_field_trials.h
+++ b/chromium/weblayer/browser/weblayer_field_trials.h
@@ -14,11 +14,11 @@ namespace weblayer {
// functions are stubs, as WebLayer has no specific field trials.
class WebLayerFieldTrials : public variations::PlatformFieldTrials {
public:
- WebLayerFieldTrials() {}
- ~WebLayerFieldTrials() override {}
+ WebLayerFieldTrials() = default;
+ ~WebLayerFieldTrials() override = default;
// variations::PlatformFieldTrials:
- void SetupFieldTrials() override {}
+ void SetupFieldTrials() override;
void SetupFeatureControllingFieldTrials(
bool has_seed,
base::FeatureList* feature_list) override {}
diff --git a/chromium/weblayer/browser/weblayer_impl_android.cc b/chromium/weblayer/browser/weblayer_impl_android.cc
index 3f54f31edf7..d07dc34ef0e 100644
--- a/chromium/weblayer/browser/weblayer_impl_android.cc
+++ b/chromium/weblayer/browser/weblayer_impl_android.cc
@@ -13,6 +13,7 @@
#include "weblayer/browser/java/jni/WebLayerImpl_jni.h"
#include "weblayer/browser/url_bar/page_info_client_impl.h"
#include "weblayer/browser/user_agent.h"
+#include "weblayer/common/crash_reporter/crash_keys.h"
using base::android::JavaParamRef;
@@ -30,7 +31,7 @@ static jboolean JNI_WebLayerImpl_IsRemoteDebuggingEnabled(JNIEnv* env) {
static void JNI_WebLayerImpl_SetIsWebViewCompatMode(JNIEnv* env,
jboolean value) {
static crash_reporter::CrashKeyString<1> crash_key(
- "WEBLAYER_WEB_VIEW_COMPAT_MODE");
+ crash_keys::kWeblayerWebViewCompatMode);
crash_key.Set(value ? "1" : "0");
}
@@ -44,7 +45,6 @@ static void JNI_WebLayerImpl_RegisterExternalExperimentIDs(
JNIEnv* env,
const JavaParamRef<jstring>& jtrial_name,
const JavaParamRef<jintArray>& jexperiment_ids) {
- const std::string trial_name_utf8(ConvertJavaStringToUTF8(env, jtrial_name));
std::vector<int> experiment_ids;
// A null |jexperiment_ids| is the same as an empty list.
if (jexperiment_ids) {
@@ -52,8 +52,8 @@ static void JNI_WebLayerImpl_RegisterExternalExperimentIDs(
&experiment_ids);
}
- WebLayerMetricsServiceClient::GetInstance()
- ->RegisterSyntheticMultiGroupFieldTrial(trial_name_utf8, experiment_ids);
+ WebLayerMetricsServiceClient::GetInstance()->RegisterExternalExperiments(
+ experiment_ids);
}
base::string16 GetClientApplicationName() {
diff --git a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc
index 33de9c7e102..37d3676f4f0 100644
--- a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc
+++ b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc
@@ -38,14 +38,10 @@ void OpenLoginPage(content::WebContents* web_contents) {
// ChromeSecurityBlockingPageFactory::OpenLoginPage(), from which this is
// adapted.
#if defined(OS_ANDROID)
- // In Chrome this opens in a new tab, but WebLayer's TabImpl has no support
- // for opening new tabs (its OpenURLFromTab() method DCHECKs if the
- // disposition is not |CURRENT_TAB|).
- // TODO(crbug.com/1047130): Revisit if TabImpl gets support for opening URLs
- // in new tabs.
- content::OpenURLParams params(
- GetCaptivePortalLoginPageUrlInternal(), content::Referrer(),
- WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_LINK, false);
+ content::OpenURLParams params(GetCaptivePortalLoginPageUrlInternal(),
+ content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
web_contents->OpenURL(params);
#else
NOTIMPLEMENTED();
@@ -201,6 +197,16 @@ WebLayerSecurityBlockingPageFactory::CreateBlockedInterceptionBlockingPage(
return interstitial_page;
}
+std::unique_ptr<security_interstitials::InsecureFormBlockingPage>
+WebLayerSecurityBlockingPageFactory::CreateInsecureFormBlockingPage(
+ content::WebContents* web_contents,
+ const GURL& request_url) {
+ // TODO(crbug.com/1093102): Insecure form warnings are not yet implemented in
+ // Weblayer.
+ NOTREACHED();
+ return nullptr;
+}
+
#if defined(OS_ANDROID)
// static
GURL WebLayerSecurityBlockingPageFactory::
diff --git a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.h b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.h
index d0a2a5ad291..e154d433c32 100644
--- a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.h
+++ b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.h
@@ -11,6 +11,7 @@
#include "components/security_interstitials/content/bad_clock_blocking_page.h"
#include "components/security_interstitials/content/blocked_interception_blocking_page.h"
#include "components/security_interstitials/content/captive_portal_blocking_page.h"
+#include "components/security_interstitials/content/insecure_form_blocking_page.h"
#include "components/security_interstitials/content/mitm_software_blocking_page.h"
#include "components/security_interstitials/content/security_blocking_page_factory.h"
#include "components/security_interstitials/content/ssl_blocking_page.h"
@@ -73,6 +74,9 @@ class WebLayerSecurityBlockingPageFactory : public SecurityBlockingPageFactory {
const GURL& request_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
const net::SSLInfo& ssl_info) override;
+ std::unique_ptr<security_interstitials::InsecureFormBlockingPage>
+ CreateInsecureFormBlockingPage(content::WebContents* web_contents,
+ const GURL& request_url) override;
#if defined(OS_ANDROID)
// Returns the URL that will be navigated to when the user clicks on the
diff --git a/chromium/weblayer/browser/weblayer_site_settings_client.cc b/chromium/weblayer/browser/weblayer_site_settings_client.cc
deleted file mode 100644
index bb65eeacf32..00000000000
--- a/chromium/weblayer/browser/weblayer_site_settings_client.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-#include "weblayer/browser/java/jni/WebLayerSiteSettingsClient_jni.h"
-
-#include "components/content_settings/core/common/pref_names.h"
-#include "components/embedder_support/android/browser_context/browser_context_handle.h"
-#include "components/prefs/pref_service.h"
-#include "components/user_prefs/user_prefs.h"
-#include "content/public/browser/browser_context.h"
-
-using base::android::JavaParamRef;
-
-namespace weblayer {
-
-namespace {
-
-PrefService* GetPrefService(
- const JavaParamRef<jobject>& jbrowser_context_handle) {
- content::BrowserContext* browser_context =
- browser_context::BrowserContextFromJavaHandle(jbrowser_context_handle);
- if (!browser_context)
- return nullptr;
- return user_prefs::UserPrefs::Get(browser_context);
-}
-
-} // namespace
-
-static jboolean JNI_WebLayerSiteSettingsClient_GetBlockThirdPartyCookies(
- JNIEnv* env,
- const JavaParamRef<jobject>& jbrowser_context_handle) {
- PrefService* pref_service = GetPrefService(jbrowser_context_handle);
- if (!pref_service)
- return false;
- return pref_service->GetBoolean(prefs::kBlockThirdPartyCookies);
-}
-
-static void JNI_WebLayerSiteSettingsClient_SetBlockThirdPartyCookies(
- JNIEnv* env,
- const JavaParamRef<jobject>& jbrowser_context_handle,
- jboolean value) {
- PrefService* pref_service = GetPrefService(jbrowser_context_handle);
- if (!pref_service)
- return;
- pref_service->SetBoolean(prefs::kBlockThirdPartyCookies, value);
-}
-
-static jint JNI_WebLayerSiteSettingsClient_GetCookieControlsMode(
- JNIEnv* env,
- const JavaParamRef<jobject>& jbrowser_context_handle) {
- PrefService* pref_service = GetPrefService(jbrowser_context_handle);
- if (!pref_service)
- return 0;
- return pref_service->GetInteger(prefs::kCookieControlsMode);
-}
-
-static void JNI_WebLayerSiteSettingsClient_SetCookieControlsMode(
- JNIEnv* env,
- const JavaParamRef<jobject>& jbrowser_context_handle,
- jint value) {
- PrefService* pref_service = GetPrefService(jbrowser_context_handle);
- if (!pref_service)
- return;
- pref_service->SetInteger(prefs::kCookieControlsMode, value);
-}
-
-} // namespace weblayer
diff --git a/chromium/weblayer/browser/weblayer_speech_recognition_manager_delegate.cc b/chromium/weblayer/browser/weblayer_speech_recognition_manager_delegate.cc
index 7738351203b..8c941e61ea8 100644
--- a/chromium/weblayer/browser/weblayer_speech_recognition_manager_delegate.cc
+++ b/chromium/weblayer/browser/weblayer_speech_recognition_manager_delegate.cc
@@ -8,7 +8,6 @@
#include "base/bind.h"
#include "base/macros.h"
-#include "base/task/post_task.h"
#include "build/build_config.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -84,8 +83,8 @@ void WebLayerSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
// Check that the render frame type is appropriate, and whether or not we
// need to request permission from the user.
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&CheckRenderFrameType, std::move(callback),
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&CheckRenderFrameType, std::move(callback),
render_process_id, render_frame_id));
}
@@ -109,8 +108,8 @@ void WebLayerSpeechRecognitionManagerDelegate::CheckRenderFrameType(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Regular tab contents.
- base::PostTask(
- FROM_HERE, {BrowserThread::IO},
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(std::move(callback), true /* check_permission */,
true /* allowed */));
}