summaryrefslogtreecommitdiff
path: root/chromium/weblayer
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
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-c30a6232df03e1efbd9f3b226777b07e087a1122.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/weblayer')
-rw-r--r--chromium/weblayer/API_OWNERS1
-rw-r--r--chromium/weblayer/BUILD.gn173
-rw-r--r--chromium/weblayer/DEPS1
-rw-r--r--chromium/weblayer/OWNERS1
-rw-r--r--chromium/weblayer/README.md46
-rw-r--r--chromium/weblayer/app/DEPS4
-rw-r--r--chromium/weblayer/app/content_main_delegate_impl.cc82
-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
-rw-r--r--chromium/weblayer/common/crash_reporter/crash_keys.cc38
-rw-r--r--chromium/weblayer/common/crash_reporter/crash_keys.h4
-rw-r--r--chromium/weblayer/common/crash_reporter/crash_reporter_client.cc7
-rw-r--r--chromium/weblayer/grit_resources_allowlist.txt2
-rw-r--r--chromium/weblayer/grit_strings_allowlist.txt (renamed from chromium/weblayer/grit_strings_whitelist.txt)7
-rw-r--r--chromium/weblayer/public/browser.h8
-rw-r--r--chromium/weblayer/public/java/AndroidManifest.xml11
-rw-r--r--chromium/weblayer/public/java/BUILD.gn14
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/Browser.java21
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java96
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java4
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java10
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java49
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/Profile.java97
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java34
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java39
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/Tab.java306
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java24
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java37
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java34
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java24
-rw-r--r--chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java70
-rw-r--r--chromium/weblayer/public/java/res/values-night/colors.xml10
-rw-r--r--chromium/weblayer/public/java/res/values-night/values.xml10
-rw-r--r--chromium/weblayer/public/java/res/values-v27/styles.xml12
-rw-r--r--chromium/weblayer/public/java/res/values-v28/styles.xml18
-rw-r--r--chromium/weblayer/public/java/res/values/colors.xml16
-rw-r--r--chromium/weblayer/public/java/res/values/ids.xml8
-rw-r--r--chromium/weblayer/public/java/res/values/styles.xml13
-rw-r--r--chromium/weblayer/public/java/res/values/values.xml10
-rw-r--r--chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java2
-rw-r--r--chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java3
-rw-r--r--chromium/weblayer/public/javatestutil/BUILD.gn1
-rw-r--r--chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java39
-rw-r--r--chromium/weblayer/public/js_communication/web_message.cc13
-rw-r--r--chromium/weblayer/public/js_communication/web_message.h21
-rw-r--r--chromium/weblayer/public/js_communication/web_message_host.h26
-rw-r--r--chromium/weblayer/public/js_communication/web_message_host_factory.h35
-rw-r--r--chromium/weblayer/public/js_communication/web_message_reply_proxy.h25
-rw-r--r--chromium/weblayer/public/navigation.h5
-rw-r--r--chromium/weblayer/public/navigation_controller.h5
-rw-r--r--chromium/weblayer/public/navigation_observer.h7
-rw-r--r--chromium/weblayer/public/new_tab_delegate.h4
-rw-r--r--chromium/weblayer/public/profile.h34
-rw-r--r--chromium/weblayer/public/tab.h46
-rw-r--r--chromium/weblayer/renderer/DEPS1
-rw-r--r--chromium/weblayer/renderer/content_renderer_client_impl.cc27
-rw-r--r--chromium/weblayer/renderer/content_renderer_client_impl.h3
-rw-r--r--chromium/weblayer/renderer/error_page_helper.cc20
-rw-r--r--chromium/weblayer/renderer/error_page_helper.h3
-rw-r--r--chromium/weblayer/renderer/weblayer_render_frame_observer.cc2
-rw-r--r--chromium/weblayer/shell/android/BUILD.gn2
-rw-r--r--chromium/weblayer/shell/android/browsertests_apk/metrics_browsertest.cc93
-rw-r--r--chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.cc61
-rw-r--r--chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.h36
-rw-r--r--chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java19
-rw-r--r--chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java25
-rw-r--r--chromium/weblayer/shell/android/shell_apk/AndroidManifest.xml3
-rw-r--r--chromium/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml1
-rw-r--r--chromium/weblayer/shell/android/shell_apk/res/menu/app_menu.xml2
-rw-r--r--chromium/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java40
-rw-r--r--chromium/weblayer/shell/app/shell_main_params.cc22
-rw-r--r--chromium/weblayer/shell/browser/shell.cc72
-rw-r--r--chromium/weblayer/shell/browser/shell.h16
-rw-r--r--chromium/weblayer/shell/browser/shell_views.cc32
-rw-r--r--chromium/weblayer/shell/common/shell_switches.cc3
-rw-r--r--chromium/weblayer/shell/common/shell_switches.h2
-rw-r--r--chromium/weblayer/test/BUILD.gn31
-rwxr-xr-xchromium/weblayer/tools/run_weblayer_shell.py4
-rw-r--r--chromium/weblayer/weblayer_resource_exclusions.gni56
350 files changed, 14283 insertions, 1758 deletions
diff --git a/chromium/weblayer/API_OWNERS b/chromium/weblayer/API_OWNERS
index cd7ad194b32..1447b758536 100644
--- a/chromium/weblayer/API_OWNERS
+++ b/chromium/weblayer/API_OWNERS
@@ -5,4 +5,3 @@ darin@chromium.org
jam@chromium.org
sky@chromium.org
tobiasjs@chromium.org
-torne@chromium.org
diff --git a/chromium/weblayer/BUILD.gn b/chromium/weblayer/BUILD.gn
index 072562b885f..b29d3adfa05 100644
--- a/chromium/weblayer/BUILD.gn
+++ b/chromium/weblayer/BUILD.gn
@@ -46,11 +46,11 @@ if (is_android) {
# components_strings contains strings from all components. WebLayer
# will never display most of them, so we try to limit the included
# strings.
- whitelist = rebase_path("grit_strings_whitelist.txt", root_build_dir)
- inputs = [ "//weblayer/grit_strings_whitelist.txt" ]
+ allowlist = rebase_path("grit_strings_allowlist.txt", root_build_dir)
+ inputs = [ "//weblayer/grit_strings_allowlist.txt" ]
grit_flags = [
"-w",
- whitelist,
+ allowlist,
]
outputs =
weblayer_components_strings_java_resources +
@@ -65,11 +65,11 @@ if (is_android) {
# components_chromium_strings contains Chromium-specific strings. WebLayer
# will never display most of them, so we try to limit the included
# strings.
- whitelist = rebase_path("grit_strings_whitelist.txt", root_build_dir)
- inputs = [ "//weblayer/grit_strings_whitelist.txt" ]
+ allowlist = rebase_path("grit_strings_allowlist.txt", root_build_dir)
+ inputs = [ "//weblayer/grit_strings_allowlist.txt" ]
grit_flags = [
"-w",
- whitelist,
+ allowlist,
]
outputs = [ "grit/components_chromium_strings.h" ] +
process_file_template(
@@ -139,12 +139,21 @@ source_set("weblayer_lib_base") {
"browser/browser_context_impl.h",
"browser/browser_impl.cc",
"browser/browser_impl.h",
+ "browser/browser_list.cc",
+ "browser/browser_list.h",
+ "browser/browser_list_observer.h",
"browser/browser_main_parts_impl.cc",
"browser/browser_main_parts_impl.h",
"browser/browser_process.cc",
"browser/browser_process.h",
+ "browser/browsing_data_remover_delegate.cc",
+ "browser/browsing_data_remover_delegate.h",
+ "browser/browsing_data_remover_delegate_factory.cc",
+ "browser/browsing_data_remover_delegate_factory.h",
"browser/client_hints_factory.cc",
"browser/client_hints_factory.h",
+ "browser/confirm_infobar_android.cc",
+ "browser/confirm_infobar_android.h",
"browser/content_browser_client_impl.cc",
"browser/content_browser_client_impl.h",
"browser/content_browser_client_impl_receiver_bindings.cc",
@@ -165,16 +174,26 @@ source_set("weblayer_lib_base") {
"browser/file_select_helper.h",
"browser/host_content_settings_map_factory.cc",
"browser/host_content_settings_map_factory.h",
+ "browser/http_auth_handler_impl.cc",
+ "browser/http_auth_handler_impl.h",
"browser/i18n_util.cc",
"browser/i18n_util.h",
+ "browser/infobar_android.cc",
+ "browser/infobar_android.h",
+ "browser/infobar_container_android.cc",
+ "browser/infobar_container_android.h",
"browser/javascript_tab_modal_dialog_manager_delegate_android.cc",
"browser/javascript_tab_modal_dialog_manager_delegate_android.h",
+ "browser/js_communication/web_message_host_factory_wrapper.cc",
+ "browser/js_communication/web_message_host_factory_wrapper.h",
"browser/navigation_controller_impl.cc",
"browser/navigation_controller_impl.h",
"browser/navigation_impl.cc",
"browser/navigation_impl.h",
"browser/page_load_metrics_initialize.cc",
"browser/page_load_metrics_initialize.h",
+ "browser/password_manager_driver_factory.cc",
+ "browser/password_manager_driver_factory.h",
"browser/permissions/geolocation_permission_context_delegate.cc",
"browser/permissions/geolocation_permission_context_delegate.h",
"browser/permissions/permission_decision_auto_blocker_factory.cc",
@@ -187,8 +206,12 @@ source_set("weblayer_lib_base") {
"browser/persistence/browser_persistence_common.h",
"browser/persistence/browser_persister.cc",
"browser/persistence/browser_persister.h",
+ "browser/persistence/browser_persister_file_utils.cc",
+ "browser/persistence/browser_persister_file_utils.h",
"browser/persistence/minimal_browser_persister.cc",
"browser/persistence/minimal_browser_persister.h",
+ "browser/popup_navigation_delegate_impl.cc",
+ "browser/popup_navigation_delegate_impl.h",
"browser/profile_disk_operations.cc",
"browser/profile_disk_operations.h",
"browser/profile_impl.cc",
@@ -223,6 +246,9 @@ source_set("weblayer_lib_base") {
"browser/weblayer_browser_interface_binders.h",
"browser/weblayer_content_browser_overlay_manifest.cc",
"browser/weblayer_content_browser_overlay_manifest.h",
+ "browser/weblayer_features.cc",
+ "browser/weblayer_features.h",
+ "browser/weblayer_field_trials.cc",
"browser/weblayer_field_trials.h",
"browser/weblayer_security_blocking_page_factory.cc",
"browser/weblayer_security_blocking_page_factory.h",
@@ -251,6 +277,11 @@ source_set("weblayer_lib_base") {
"public/download_delegate.h",
"public/error_page_delegate.h",
"public/fullscreen_delegate.h",
+ "public/js_communication/web_message.cc",
+ "public/js_communication/web_message.h",
+ "public/js_communication/web_message_host.h",
+ "public/js_communication/web_message_host_factory.h",
+ "public/js_communication/web_message_reply_proxy.h",
"public/main.h",
"public/navigation.h",
"public/navigation_controller.h",
@@ -289,7 +320,9 @@ source_set("weblayer_lib_base") {
"//components/autofill/content/browser",
"//components/autofill/content/renderer",
"//components/autofill/core/browser",
+ "//components/autofill/core/common",
"//components/base32",
+ "//components/blocked_content",
"//components/browsing_data/content",
"//components/captive_portal/core:buildflags",
"//components/cdm/renderer",
@@ -307,6 +340,8 @@ source_set("weblayer_lib_base") {
"//components/error_page/common",
"//components/find_in_page",
"//components/infobars/core",
+ "//components/js_injection/browser",
+ "//components/js_injection/renderer",
"//components/keyed_service/content",
"//components/language/core/browser",
"//components/metrics",
@@ -315,6 +350,7 @@ source_set("weblayer_lib_base") {
"//components/page_info",
"//components/page_load_metrics/browser",
"//components/page_load_metrics/renderer",
+ "//components/password_manager/content/browser",
"//components/permissions",
"//components/policy/core/browser",
"//components/pref_registry:pref_registry",
@@ -326,6 +362,7 @@ source_set("weblayer_lib_base") {
"//components/security_state/content",
"//components/services/storage/public/mojom",
"//components/sessions",
+ "//components/site_isolation",
"//components/spellcheck:buildflags",
"//components/ssl_errors",
"//components/startup_metric_utils/browser",
@@ -334,11 +371,15 @@ source_set("weblayer_lib_base") {
"//components/translate/content/renderer",
"//components/translate/core/browser",
"//components/translate/core/common",
+ "//components/ukm",
+ "//components/ukm/content",
+ "//components/unified_consent:unified_consent",
"//components/user_prefs",
"//components/variations",
"//components/variations/net",
"//components/variations/service",
"//components/version_info",
+ "//components/viz/common",
"//components/web_cache/browser",
"//components/webrtc",
"//content:content_resources",
@@ -357,6 +398,7 @@ source_set("weblayer_lib_base") {
"//net:net_resources",
"//sandbox",
"//services/metrics/public/cpp:metrics_cpp",
+ "//services/network/public/cpp",
"//services/network/public/mojom",
"//services/preferences/tracked",
"//services/service_manager/embedder:embedder_result_codes",
@@ -408,7 +450,6 @@ source_set("weblayer_lib_base") {
"browser/android/exception_filter.h",
"browser/android/metrics/uma_utils.cc",
"browser/android/metrics/uma_utils.h",
- "browser/android/metrics/weblayer_metrics_service_accessor.h",
"browser/android/metrics/weblayer_metrics_service_client.cc",
"browser/android/metrics/weblayer_metrics_service_client.h",
"browser/android/permission_request_utils.cc",
@@ -432,17 +473,42 @@ source_set("weblayer_lib_base") {
"browser/error_page_callback_proxy.h",
"browser/fullscreen_callback_proxy.cc",
"browser/fullscreen_callback_proxy.h",
+ "browser/infobar_service.cc",
+ "browser/infobar_service.h",
+ "browser/js_communication/web_message_host_factory_proxy.cc",
+ "browser/js_communication/web_message_host_factory_proxy.h",
+ "browser/js_communication/web_message_reply_proxy_impl.cc",
+ "browser/js_communication/web_message_reply_proxy_impl.h",
"browser/new_tab_callback_proxy.cc",
"browser/new_tab_callback_proxy.h",
+ "browser/safe_browsing/real_time_url_lookup_service_factory.cc",
+ "browser/safe_browsing/real_time_url_lookup_service_factory.h",
+ "browser/safe_browsing/safe_browsing_blocking_page.cc",
+ "browser/safe_browsing/safe_browsing_blocking_page.h",
+ "browser/safe_browsing/safe_browsing_navigation_throttle.cc",
+ "browser/safe_browsing/safe_browsing_navigation_throttle.h",
+ "browser/safe_browsing/safe_browsing_service.cc",
+ "browser/safe_browsing/safe_browsing_service.h",
+ "browser/safe_browsing/safe_browsing_subresource_helper.cc",
+ "browser/safe_browsing/safe_browsing_subresource_helper.h",
+ "browser/safe_browsing/safe_browsing_ui_manager.cc",
+ "browser/safe_browsing/safe_browsing_ui_manager.h",
+ "browser/safe_browsing/url_checker_delegate_impl.cc",
+ "browser/safe_browsing/url_checker_delegate_impl.h",
"browser/tab_callback_proxy.cc",
"browser/tab_callback_proxy.h",
+ "browser/translate_compact_infobar.cc",
+ "browser/translate_compact_infobar.h",
+ "browser/translate_utils.cc",
+ "browser/translate_utils.h",
"browser/url_bar/page_info_client_impl.cc",
"browser/url_bar/page_info_client_impl.h",
+ "browser/verdict_cache_manager_factory.cc",
+ "browser/verdict_cache_manager_factory.h",
"browser/weblayer_factory_impl_android.cc",
"browser/weblayer_factory_impl_android.h",
"browser/weblayer_impl_android.cc",
"browser/weblayer_impl_android.h",
- "browser/weblayer_site_settings_client.cc",
"browser/webrtc/media_stream_manager.cc",
"browser/webrtc/media_stream_manager.h",
"common/crash_reporter/crash_keys.cc",
@@ -456,7 +522,8 @@ source_set("weblayer_lib_base") {
"//android_webview:generate_aw_resources",
"//android_webview:generate_aw_strings",
"//components/android_system_error_page",
- "//components/autofill/android:provider",
+ "//components/autofill/android/provider",
+ "//components/browser_ui/client_certificate/android",
"//components/browser_ui/site_settings/android",
"//components/cdm/browser",
"//components/content_settings/android",
@@ -468,6 +535,7 @@ source_set("weblayer_lib_base") {
"//components/embedder_support/android:web_contents_delegate",
"//components/embedder_support/android/metrics",
"//components/external_intents/android",
+ "//components/infobars/content",
"//components/javascript_dialogs",
"//components/location/android:settings",
"//components/metrics",
@@ -476,15 +544,28 @@ source_set("weblayer_lib_base") {
"//components/page_info/android",
"//components/permissions/android:native",
"//components/resources:android_resources",
+ "//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/common:interfaces",
"//components/safe_browsing/content/renderer:throttles",
+ "//components/safe_browsing/core:ping_manager",
+ "//components/safe_browsing/core:verdict_cache_manager",
+ "//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/safe_browsing/core/realtime:policy_engine",
+ "//components/safe_browsing/core/realtime:url_lookup_service",
+ "//components/security_interstitials/content:security_interstitial_page",
+ "//components/security_interstitials/core:unsafe_resource",
+ "//components/security_interstitials/core/",
"//components/security_state/content/android",
"//components/version_info/android:channel_getter",
"//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
"//ui/android",
"//weblayer/browser/java:jni",
- "//weblayer/browser/safe_browsing:safe_browsing",
]
} else {
deps += [ "//ui/views/controls/webview" ]
@@ -526,6 +607,25 @@ source_set("weblayer_lib_base") {
}
if (is_android) {
+ source_set("weblayer_android_test_jni_impl") {
+ testonly = true
+ sources = [
+ "browser/test/test_infobar.cc",
+ "browser/test/test_infobar.h",
+ "browser/test/test_weblayer_impl.cc",
+ ]
+ deps = [
+ ":weblayer_lib_base",
+ "//base",
+ "//components/infobars/core",
+ "//components/translate/core/browser",
+ "//content/public/browser",
+ "//content/test:test_support",
+ "//testing/gtest",
+ "//weblayer/browser/java:test_jni",
+ ]
+ }
+
# Lib used in standalone WebView which allows manual JNI registration.
static_library("weblayer_lib_webview") {
public_deps = [ ":weblayer_lib_base" ]
@@ -552,6 +652,33 @@ if (is_android) {
}
}
+ static_library("weblayer_lib_webview_test") {
+ testonly = true
+ public_deps = [ ":weblayer_lib_base" ]
+ deps = [
+ ":weblayer_android_test_jni_impl",
+ "//base",
+ "//weblayer/browser/java:jni",
+ "//weblayer/browser/java:weblayer_jni_registration",
+ ]
+ sources = [
+ "$target_gen_dir/browser/java/weblayer_jni_registration.h",
+ "browser/web_view_compatibility_helper_impl.cc",
+ "browser/web_view_compatibility_helper_impl.h",
+ ]
+ defines = [ "WEBLAYER_MANUAL_JNI_REGISTRATION" ]
+
+ # Explicit dependency required for JNI registration to be able to
+ # find the native side functions.
+ if (is_component_build) {
+ deps += [
+ "//device/gamepad",
+ "//media/midi",
+ "//ui/events/devices",
+ ]
+ }
+ }
+
# Lib used in Monochrome which does not support manual JNI registration.
# Separate from the standalone WebView version to reduce APK size.
static_library("weblayer_lib") {
@@ -562,6 +689,18 @@ if (is_android) {
"browser/web_view_compatibility_helper_impl.h",
]
}
+
+ shared_library("libweblayer_test") {
+ testonly = true
+ sources = [ "app/entry_point.cc" ]
+ deps = [
+ ":weblayer_lib_webview_test",
+ "//base",
+ "//content/public/app",
+ ]
+ configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+ configs += [ "//build/config/android:hide_all_but_jni" ]
+ }
} else {
source_set("weblayer_lib") {
public_deps = [ ":weblayer_lib_base" ]
@@ -581,19 +720,5 @@ grit("resources") {
]
deps = [ "//weblayer/browser/webui:mojo_bindings_js" ]
}
-
# TODO(jam): move weblayer_shell_resources_grit and copy_shell_resources here in
# a way that's shareable?
-
-if (is_android) {
- shared_library("libweblayer") {
- sources = [ "app/entry_point.cc" ]
- deps = [
- ":weblayer_lib_webview",
- "//base",
- "//content/public/app",
- ]
- configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
- configs += [ "//build/config/android:hide_all_but_jni" ]
- }
-}
diff --git a/chromium/weblayer/DEPS b/chromium/weblayer/DEPS
index 1120ca183e7..30e0bf72f1f 100644
--- a/chromium/weblayer/DEPS
+++ b/chromium/weblayer/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/content_settings/core/browser",
+ "+components/js_injection",
"+components/omnibox/browser",
"+components/page_info/android",
"+components/security_state/core/security_state.h",
diff --git a/chromium/weblayer/OWNERS b/chromium/weblayer/OWNERS
index b97477bc192..d5ecf5efe0c 100644
--- a/chromium/weblayer/OWNERS
+++ b/chromium/weblayer/OWNERS
@@ -8,7 +8,6 @@ nator@chromium.org
sky@chromium.org
timvolodine@chromium.org
tobiasjs@chromium.org
-torne@chromium.org
# TEAM: weblayer-dev@chromium.org
# COMPONENT: Internals>WebLayer
diff --git a/chromium/weblayer/README.md b/chromium/weblayer/README.md
index db5bd0d1c2e..072ccdfc69d 100644
--- a/chromium/weblayer/README.md
+++ b/chromium/weblayer/README.md
@@ -2,13 +2,13 @@
WebLayer is a high level embedding API to support building a browser.
-Unlike src/content, which is only concerned with a sandboxed multi-process web
+Unlike `src/content`, which is only concerned with a sandboxed multi-process web
platform, WebLayer includes modern browser features and Google integration.
It's the reusable version of Chrome, which might share some portions of the UI
and also its support for all the modern HTML5 and browser features (e.g. UI for
permissions, autofill, safe browsing etc...).
-While it's built on top of src/content, the expectation is that the API will
+While it's built on top of `src/content`, the expectation is that the API will
hide the Content API.
## Design Goals
@@ -31,11 +31,11 @@ Bug tracker: [Internals>WebLayer](https://bugs.chromium.org/p/chromium/issues/li
`shell` sample app
-'test' test harnesses and test-only helper code
+`test` test harnesses and test-only helper code
-'tools' helper scripts
+`tools` helper scripts
-'app' internal code which runs at the beginning of each process
+`app` internal code which runs at the beginning of each process
`browser` internal code which runs in the browser process
@@ -49,26 +49,27 @@ Bug tracker: [Internals>WebLayer](https://bugs.chromium.org/p/chromium/issues/li
If you haven't done this already, you first need to set up an Android build. If
you are a Google employee, reach out to weblayer-team@google.com for internal
-instructions. Otherwise follow the [Android build instructions](/docs/android_build_instructions.md).
+instructions. Otherwise follow the
+[Android build instructions](https://source.chromium.org/chromium/chromium/src/+/master:docs/android_build_instructions.md).
## Building and Testing
To run the sample app:
-$ autoninja -C out/Default run_weblayer_shell
-$ out/Default/bin/run_weblayer_shell
+ $ autoninja -C out/Default run_weblayer_shell
+ $ out/Default/bin/run_weblayer_shell
To run instrumentation tests:
-$ autoninja -C out/Default weblayer_instrumentation_test_apk
-$ out/Default/bin/run_weblayer_instrumentation_test_apk
+ $ autoninja -C out/Default weblayer_instrumentation_test_apk
+ $ out/Default/bin/run_weblayer_instrumentation_test_apk
Note: this may not work on some versions of Android. If you see an error setting
the WebView provider when running instrumentation tests, try running the tests
using the WebLayer support APK which uses a different loading path:
-$ autoninja -C out/Default weblayer_support_instrumentation_test_apk
-$ out/Default/bin/run_weblayer_support_instrumentation_test_apk
+ $ autoninja -C out/Default weblayer_support_instrumentation_test_apk
+ $ out/Default/bin/run_weblayer_support_instrumentation_test_apk
The test script will build and install all necessary APKs.
@@ -81,8 +82,8 @@ To run WPT on android against weblayer do the following:
$ out/Default/bin/run_weblayer_shell
$ testing/scripts/run_android_wpt.py --webdriver-binary=out/Default/clang_x64/chromedriver --product android_weblayer --isolated-script-test-output /tmp/weblayer_out.json --include ./third_party/blink/web_tests/external/wpt/$WPT_TEST
-`run_android_wpt.py` does not install weblayer-shell, you need to do that
-yourself (executing run_weblayer_shell will do that).
+`run_android_wpt.py` does not install `weblayer-shell`, you need to do that
+yourself (executing `run_weblayer_shell` will do that).
To run against clank:
@@ -96,11 +97,10 @@ test execution.
## Telemetry
Telemetry is run against WebLayer, currently on the bot
-[android-pixel2_weblayer-perf]
-(https://ci.chromium.org/p/chrome/builders/ci/android-pixel2_weblayer-perf).
+[`android-pixel2_weblayer-perf`](https://ci.chromium.org/p/chrome/builders/ci/android-pixel2_weblayer-perf).
-Telemetry currently only runs on real hardware. Bug [1067712]
-(https://bugs.chromium.org/p/chromium/issues/detail?id=1067712) is for
+Telemetry currently only runs on real hardware. Bug
+[1067712](https://bugs.chromium.org/p/chromium/issues/detail?id=1067712) is for
adding support for emulators.
### Tricks:
@@ -109,11 +109,11 @@ To see the set of stories executed, click on a successful run, search for
`performance_weblayer_test_suite` and click on the `json.output`
link.
-Googlers can submit jobs against your own patch using [pinpoint]
-(https://pinpoint-dot-chromeperf.appspot.com/). At the time of this
-writing, logcat is *not* captured for successful runs ([1067024]
-(https://bugs.chromium.org/p/chromium/issues/detail?id=1067024)). Submitting
-a pinpoint run against a patch with a CHECK will generate
+Googlers can submit jobs against your own patch using
+[pinpoint](https://pinpoint-dot-chromeperf.appspot.com/). At the time of this
+writing, logcat is *not* captured for successful runs
+([1067024](https://bugs.chromium.org/p/chromium/issues/detail?id=1067024)).
+Submitting a pinpoint run against a patch with a CHECK will generate
logcat. For such a run, the logcat is viewable by way of:
1. Click on Id next to `task` under `Test`.
diff --git a/chromium/weblayer/app/DEPS b/chromium/weblayer/app/DEPS
index ef333d8c580..6ab38676f87 100644
--- a/chromium/weblayer/app/DEPS
+++ b/chromium/weblayer/app/DEPS
@@ -1,8 +1,12 @@
include_rules = [
+ "+components/autofill/core/common",
"+components/startup_metric_utils",
"+components/version_info",
+ "+components/viz/common",
"+content/public",
"+media/base/media_switches.h",
"+sandbox",
+ "+services/network/public/cpp",
+ "+third_party/blink/public",
"+ui/base",
]
diff --git a/chromium/weblayer/app/content_main_delegate_impl.cc b/chromium/weblayer/app/content_main_delegate_impl.cc
index 25c7412df72..020ec73c5b5 100644
--- a/chromium/weblayer/app/content_main_delegate_impl.cc
+++ b/chromium/weblayer/app/content_main_delegate_impl.cc
@@ -20,6 +20,8 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "media/base/media_switches.h"
+#include "services/network/public/cpp/features.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"
#include "weblayer/browser/content_browser_client_impl.h"
@@ -31,11 +33,14 @@
#if defined(OS_ANDROID)
#include "base/android/apk_assets.h"
+#include "base/android/build_info.h"
#include "base/android/bundle_utils.h"
#include "base/android/java_exception_reporter.h"
#include "base/android/locale_utils.h"
#include "base/i18n/rtl.h"
#include "base/posix/global_descriptors.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "components/viz/common/features.h"
#include "content/public/browser/android/compositor.h"
#include "ui/base/resource/resource_bundle_android.h"
#include "ui/base/ui_base_switches.h"
@@ -69,9 +74,11 @@ void InitLogging(MainParams* params) {
true /* Timestamp */, false /* Tick count */);
}
-// Disables each feature in |features_to_disable| unless it is already set in
-// the command line.
-void DisableFeaturesIfNotSet(
+// Enables each feature in |features_to_enable| unless it is already set in the
+// command line, and similarly disables each feature in |features_to_disable|
+// unless it is already set in the command line.
+void ConfigureFeaturesIfNotSet(
+ const std::vector<base::Feature>& features_to_enable,
const std::vector<base::Feature>& features_to_disable) {
auto* cl = base::CommandLine::ForCurrentProcess();
std::vector<std::string> enabled_features;
@@ -90,13 +97,21 @@ void DisableFeaturesIfNotSet(
disabled_features.emplace_back(f);
}
+ for (const auto& feature : features_to_enable) {
+ if (!base::Contains(disabled_features, feature.name) &&
+ !base::Contains(enabled_features, feature.name)) {
+ enabled_features.push_back(feature.name);
+ }
+ }
+ cl->AppendSwitchASCII(::switches::kEnableFeatures,
+ base::JoinString(enabled_features, ","));
+
for (const auto& feature : features_to_disable) {
if (!base::Contains(disabled_features, feature.name) &&
!base::Contains(enabled_features, feature.name)) {
disabled_features.push_back(feature.name);
}
}
-
cl->AppendSwitchASCII(::switches::kDisableFeatures,
base::JoinString(disabled_features, ","));
}
@@ -127,29 +142,60 @@ bool ContentMainDelegateImpl::BasicStartupComplete(int* exit_code) {
// implemented features.
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
// TODO(crbug.com/1025610): make notifications work with WebLayer.
+ // This also turns off Push messaging.
cl->AppendSwitch(::switches::kDisableNotifications);
- // TODO(crbug.com/1025626): and crbug.com/1051752, make speech work with
- // WebLayer.
- cl->AppendSwitch(::switches::kDisableSpeechSynthesisAPI);
// TODO(crbug.com/1057099): make presentation-api work with WebLayer.
cl->AppendSwitch(::switches::kDisablePresentationAPI);
// TODO(crbug.com/1057100): make remote-playback-api work with WebLayer.
cl->AppendSwitch(::switches::kDisableRemotePlaybackAPI);
-#if defined(OS_ANDROID)
- // TODO(crbug.com/1066263): make MediaSession work with WebLayer.
- cl->AppendSwitch(::switches::kDisableMediaSessionAPI);
-#endif
- DisableFeaturesIfNotSet({
+
+ std::vector<base::Feature> enabled_features = {};
+ std::vector<base::Feature> disabled_features = {
// TODO(crbug.com/1025619): make web-payments work with WebLayer.
::features::kWebPayments,
- // TODO(crbug.com/1025627): make webauth work with WebLayer.
- ::features::kWebAuth, ::features::kSmsReceiver,
- // TODO(crbug.com/1057106): make web-xr work with WebLayer.
- ::features::kWebXr,
+ // TODO(crbug.com/1025627): make webauth work with WebLayer.
+ ::features::kWebAuth,
+ ::features::kSmsReceiver,
+ // TODO(crbug.com/1057106): make web-xr work with WebLayer.
+ ::features::kWebXr,
+ ::features::kWebXrArModule,
+ ::features::kWebXrHitTest,
+ // TODO(crbug.com/1057770): make Background Fetch work with WebLayer.
+ ::features::kBackgroundFetch,
+ ::features::kInstalledApp,
+ // TODO(crbug.com/1091212): make Notification triggers work with
+ // WebLayer.
+ ::features::kNotificationTriggers,
+ // TODO(crbug.com/1091211): Support PeriodicBackgroundSync on WebLayer.
+ ::features::kPeriodicBackgroundSync,
+ ::features::kSmsReceiver,
+ media::kOverlayFullscreenVideo,
+#if defined(OS_ANDROID)
+ media::kPictureInPictureAPI,
+ ::features::kDisableDeJelly,
+ ::features::kDynamicColorGamut,
+#endif
+ };
+
#if defined(OS_ANDROID)
- media::kPictureInPictureAPI,
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+ base::android::SDK_VERSION_OREO) {
+ enabled_features.push_back(
+ autofill::features::kAutofillExtractAllDatalists);
+ enabled_features.push_back(
+ autofill::features::kAutofillSkipComparingInferredLabels);
+ disabled_features.push_back(
+ autofill::features::kAutofillRestrictUnownedFieldsToFormlessCheckout);
+ }
#endif
- });
+
+ ConfigureFeaturesIfNotSet(enabled_features, disabled_features);
+
+ // TODO(crbug.com/1097105): Support Web GPU on WebLayer.
+ blink::WebRuntimeFeatures::EnableWebGPU(false);
+
+ // TODO(crbug.com/1097107): Add support for Content Indexing on WebLayer.
+ blink::WebRuntimeFeatures::EnableContentIndex(false);
#if defined(OS_ANDROID)
content::Compositor::Initialize();
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 */));
}
diff --git a/chromium/weblayer/common/crash_reporter/crash_keys.cc b/chromium/weblayer/common/crash_reporter/crash_keys.cc
index 537d47248ba..d6b82b8c9c7 100644
--- a/chromium/weblayer/common/crash_reporter/crash_keys.cc
+++ b/chromium/weblayer/common/crash_reporter/crash_keys.cc
@@ -16,6 +16,44 @@ const char kAppPackageVersionCode[] = "app-package-version-code";
const char kAndroidSdkInt[] = "android-sdk-int";
+const char kWeblayerWebViewCompatMode[] = "WEBLAYER_WEB_VIEW_COMPAT_MODE";
+
+// clang-format off
+const char* const kWebLayerCrashKeyAllowList[] = {
+ kAppPackageName,
+ kAppPackageVersionCode,
+ kAndroidSdkInt,
+ kWeblayerWebViewCompatMode,
+
+ // process type
+ "ptype",
+
+ // Java exception stack traces
+ "exception_info",
+
+ // gpu
+ "gpu-driver",
+ "gpu-psver",
+ "gpu-vsver",
+ "gpu-gl-vendor",
+ "gpu-gl-renderer",
+ "oop_read_failure",
+
+ // content/:
+ "bad_message_reason",
+ "discardable-memory-allocated",
+ "discardable-memory-free",
+ "mojo-message-error",
+ "total-discardable-memory-allocated",
+
+ // crash keys needed for recording finch trials
+ "variations",
+ "num-experiments",
+
+ nullptr
+};
+// clang-format on
+
} // namespace crash_keys
void SetWebLayerCrashKeys() {
diff --git a/chromium/weblayer/common/crash_reporter/crash_keys.h b/chromium/weblayer/common/crash_reporter/crash_keys.h
index 6ab374f5c1e..406d30e86eb 100644
--- a/chromium/weblayer/common/crash_reporter/crash_keys.h
+++ b/chromium/weblayer/common/crash_reporter/crash_keys.h
@@ -16,6 +16,10 @@ extern const char kAppPackageVersionCode[];
extern const char kAndroidSdkInt[];
+extern const char kWeblayerWebViewCompatMode[];
+
+extern const char* const kWebLayerCrashKeyAllowList[];
+
} // namespace crash_keys
void SetWebLayerCrashKeys();
diff --git a/chromium/weblayer/common/crash_reporter/crash_reporter_client.cc b/chromium/weblayer/common/crash_reporter/crash_reporter_client.cc
index 3e39d1d89de..72d7685aecb 100644
--- a/chromium/weblayer/common/crash_reporter/crash_reporter_client.cc
+++ b/chromium/weblayer/common/crash_reporter/crash_reporter_client.cc
@@ -18,6 +18,7 @@
#include "components/version_info/android/channel_getter.h"
#include "components/version_info/version_info.h"
#include "components/version_info/version_info_values.h"
+#include "weblayer/common/crash_reporter/crash_keys.h"
#include "weblayer/common/weblayer_paths.h"
namespace weblayer {
@@ -44,17 +45,17 @@ class CrashReporterClientImpl : public crash_reporter::CrashReporterClient {
return base::PathService::Get(DIR_CRASH_DUMPS, crash_dir);
}
- void GetSanitizationInformation(const char* const** annotations_whitelist,
+ void GetSanitizationInformation(const char* const** crash_key_allowlist,
void** target_module,
bool* sanitize_stacks) override {
- *annotations_whitelist = nullptr;
+ *crash_key_allowlist = crash_keys::kWebLayerCrashKeyAllowList;
#if defined(COMPONENT_BUILD)
*target_module = nullptr;
#else
// The supplied address is used to identify the .so containing WebLayer.
*target_module = reinterpret_cast<void*>(&EnableCrashReporter);
#endif
- *sanitize_stacks = false;
+ *sanitize_stacks = true;
}
static CrashReporterClientImpl* Get() {
diff --git a/chromium/weblayer/grit_resources_allowlist.txt b/chromium/weblayer/grit_resources_allowlist.txt
index eae3d736e36..5f8af888039 100644
--- a/chromium/weblayer/grit_resources_allowlist.txt
+++ b/chromium/weblayer/grit_resources_allowlist.txt
@@ -1 +1,3 @@
+IDR_SAD_PLUGIN
+IDR_SAD_WEBVIEW
IDR_TRANSLATE_JS
diff --git a/chromium/weblayer/grit_strings_whitelist.txt b/chromium/weblayer/grit_strings_allowlist.txt
index ec8affc409a..a39bfcaf80f 100644
--- a/chromium/weblayer/grit_strings_whitelist.txt
+++ b/chromium/weblayer/grit_strings_allowlist.txt
@@ -6,6 +6,7 @@ IDS_BEFORERELOAD_APP_MESSAGEBOX_TITLE
IDS_BEFORERELOAD_MESSAGEBOX_TITLE
IDS_BEFOREUNLOAD_APP_MESSAGEBOX_TITLE
IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE
+IDS_CANCEL
IDS_CAPTIVE_PORTAL_BUTTON_OPEN_LOGIN_PAGE
IDS_CAPTIVE_PORTAL_HEADING_WIFI
IDS_CAPTIVE_PORTAL_HEADING_WIRED
@@ -95,6 +96,10 @@ IDS_JAVASCRIPT_MESSAGEBOX_TITLE
IDS_JAVASCRIPT_MESSAGEBOX_TITLE_IFRAME
IDS_JAVASCRIPT_MESSAGEBOX_TITLE_NONSTANDARD_URL
IDS_JAVASCRIPT_MESSAGEBOX_TITLE_NONSTANDARD_URL_IFRAME
+IDS_LOGIN_DIALOG_USERNAME_FIELD
+IDS_LOGIN_DIALOG_PASSWORD_FIELD
+IDS_LOGIN_DIALOG_TITLE
+IDS_LOGIN_DIALOG_OK_BUTTON_LABEL
IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT
IDS_MEDIA_CAPTURE_AUDIO_ONLY_INFOBAR_TEXT
IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT
@@ -202,6 +207,8 @@ IDS_PAGE_INFO_USB_DEVICE_ALLOWED_BY_POLICY_LABEL
IDS_PAGE_INFO_USB_DEVICE_SECONDARY_LABEL
IDS_PERMISSION_ALLOW
IDS_PERMISSION_DENY
+IDS_POPUPS_BLOCKED_INFOBAR_BUTTON_SHOW
+IDS_POPUPS_BLOCKED_INFOBAR_TEXT
IDS_PROTECTED_MEDIA_IDENTIFIER_PERMISSION_FRAGMENT
IDS_PROTECTED_MEDIA_IDENTIFIER_PER_DEVICE_PROVISIONING_INFOBAR_TEXT
IDS_PROTECTED_MEDIA_IDENTIFIER_PER_ORIGIN_PROVISIONING_INFOBAR_TEXT
diff --git a/chromium/weblayer/public/browser.h b/chromium/weblayer/public/browser.h
index 44e4c95fc79..8932998183b 100644
--- a/chromium/weblayer/public/browser.h
+++ b/chromium/weblayer/public/browser.h
@@ -46,12 +46,16 @@ class Browser {
virtual ~Browser() {}
- virtual Tab* AddTab(std::unique_ptr<Tab> tab) = 0;
- virtual std::unique_ptr<Tab> RemoveTab(Tab* tab) = 0;
+ virtual void AddTab(Tab* tab) = 0;
+ virtual void DestroyTab(Tab* tab) = 0;
virtual void SetActiveTab(Tab* tab) = 0;
virtual Tab* GetActiveTab() = 0;
virtual std::vector<Tab*> GetTabs() = 0;
+ // Creates a tab attached to this browser. The returned tab is owned by the
+ // browser.
+ virtual Tab* CreateTab() = 0;
+
// Called early on in shutdown, before any tabs have been removed.
virtual void PrepareForShutdown() = 0;
diff --git a/chromium/weblayer/public/java/AndroidManifest.xml b/chromium/weblayer/public/java/AndroidManifest.xml
index 13a2de7a715..90f4ab59033 100644
--- a/chromium/weblayer/public/java/AndroidManifest.xml
+++ b/chromium/weblayer/public/java/AndroidManifest.xml
@@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
@@ -68,9 +69,19 @@
<action android:name="org.chromium.weblayer.downloads.PAUSE"/>
<action android:name="org.chromium.weblayer.downloads.RESUME"/>
<action android:name="org.chromium.weblayer.downloads.CANCEL"/>
+ <!-- this needs to be in sync with IntentUtils.java-->
+ <action android:name="org.chromium.weblayer.intent_utils.ACTIVATE_TAB"/>
<!-- this needs to be in sync with MediaStreamManager.java-->
+ <!-- TODO(estade): deprecated, remove in M88. -->
<action android:name="org.chromium.weblayer.webrtc.ACTIVATE_TAB"/>
</intent-filter>
</receiver>
+
+ <service android:name="org.chromium.weblayer.MediaSessionService"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/chromium/weblayer/public/java/BUILD.gn b/chromium/weblayer/public/java/BUILD.gn
index 26cf9072075..71c7c3ca23c 100644
--- a/chromium/weblayer/public/java/BUILD.gn
+++ b/chromium/weblayer/public/java/BUILD.gn
@@ -18,11 +18,18 @@ jinja_template("weblayer_client_manifest") {
}
android_resources("client_resources") {
+ custom_package = "org.chromium.weblayer"
sources = [
+ "res/values-night/colors.xml",
+ "res/values-night/values.xml",
+ "res/values-v27/styles.xml",
+ "res/values-v28/styles.xml",
+ "res/values/colors.xml",
+ "res/values/ids.xml",
"res/values/styles.xml",
+ "res/values/values.xml",
"res/xml/weblayer_file_paths.xml",
]
- deps = [ "//components/browser_ui/styles/android:java_resources" ]
android_manifest = weblayer_client_manifest
android_manifest_dep = ":weblayer_client_manifest"
}
@@ -53,6 +60,7 @@ android_library("java") {
"org/chromium/weblayer/LoadError.java",
"org/chromium/weblayer/MediaCaptureCallback.java",
"org/chromium/weblayer/MediaCaptureController.java",
+ "org/chromium/weblayer/MediaSessionService.java",
"org/chromium/weblayer/NavigateParams.java",
"org/chromium/weblayer/Navigation.java",
"org/chromium/weblayer/NavigationCallback.java",
@@ -63,6 +71,7 @@ android_library("java") {
"org/chromium/weblayer/ObserverList.java",
"org/chromium/weblayer/Profile.java",
"org/chromium/weblayer/RemoteFragment.java",
+ "org/chromium/weblayer/ScrollNotificationType.java",
"org/chromium/weblayer/SettingType.java",
"org/chromium/weblayer/SiteSettingsActivity.java",
"org/chromium/weblayer/SiteSettingsFragment.java",
@@ -75,6 +84,9 @@ android_library("java") {
"org/chromium/weblayer/UrlBarOptions.java",
"org/chromium/weblayer/WebLayer.java",
"org/chromium/weblayer/WebLayerFileProvider.java",
+ "org/chromium/weblayer/WebMessage.java",
+ "org/chromium/weblayer/WebMessageCallback.java",
+ "org/chromium/weblayer/WebMessageReplyProxy.java",
"org/chromium/weblayer/WebViewCompatibilityHelper.java",
_version_constants_java_file,
]
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java b/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java
index 1afd12ba996..41ec9e5af4e 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -236,6 +236,27 @@ public class Browser {
}
/**
+ * Creates a new tab attached to this browser. This will call {@link TabListCallback#onTabAdded}
+ * with the new tab.
+ *
+ * @since 85
+ */
+ public @NonNull Tab createTab() {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ ITab iTab = mImpl.createTab();
+ Tab tab = Tab.getTabById(iTab.getId());
+ assert tab != null;
+ return tab;
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
* Control support for embedding use cases such as animations. This should be enabled when the
* container view of the fragment is animated in any way, needs to be rotated or blended, or
* need to control z-order with other views or other BrowserFragmentImpls. Note embedder should
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java b/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java
new file mode 100644
index 00000000000..c13b09c1207
--- /dev/null
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java
@@ -0,0 +1,96 @@
+// 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;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
+
+/**
+ * A foreground {@link Service} for the Web MediaSession API.
+ * This class is a thin wrapper that forwards lifecycle events to the WebLayer implementation, which
+ * in turn manages a system notification and {@link MediaSession}. This service will be in the
+ * foreground when the MediaSession is active.
+ * @since 85
+ */
+public class MediaSessionService extends Service {
+ // A helper to automatically pause the media session when a user removes headphones.
+ private BroadcastReceiver mAudioBecomingNoisyReceiver;
+
+ public MediaSessionService() {
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mAudioBecomingNoisyReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
+ return;
+ }
+
+ Intent i = new Intent(getApplication(), MediaSessionService.class);
+ i.setAction(intent.getAction());
+ getApplication().startService(i);
+ }
+ };
+
+ IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ registerReceiver(mAudioBecomingNoisyReceiver, filter);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ try {
+ getWebLayer().getImpl().onMediaSessionServiceDestroyed();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ unregisterReceiver(mAudioBecomingNoisyReceiver);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ try {
+ getWebLayer().getImpl().onMediaSessionServiceStarted(ObjectWrapper.wrap(this), intent);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ private WebLayer getWebLayer() {
+ WebLayer webLayer;
+ try {
+ webLayer = WebLayer.getLoadedWebLayer(getApplication());
+ } catch (UnsupportedVersionException e) {
+ throw new RuntimeException(e);
+ }
+ if (webLayer == null) {
+ throw new IllegalStateException("WebLayer not initialized");
+ }
+ return webLayer;
+ }
+}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java b/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java
index 85c880d09c5..53180286877 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java
@@ -172,6 +172,10 @@ public class Navigation extends IClientNavigation.Stub {
* NavigationCallback.onNavigationStarted}. When called during start, the header applies to both
* the initial network request as well as redirects.
*
+ * This method may be used to set the referer. If the referer is set in navigation start, it is
+ * reset during the redirect. In other words, if you need to set a referer that applies to
+ * redirects, then this must be called from {@link onNavigationRedirected}.
+ *
* @param name The name of the header. The name must be rfc 2616 compliant.
* @param value The value of the header. The value must not contain '\0', '\n' or '\r'.
*
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java
index 6b64268a386..3b85def6ef8 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java
@@ -4,6 +4,8 @@
package org.chromium.weblayer;
+import android.net.Uri;
+
import androidx.annotation.NonNull;
/**
@@ -112,4 +114,12 @@ public abstract class NavigationCallback {
* non-empty layout has finished. This is *not* called for same-document navigations.
*/
public void onFirstContentfulPaint() {}
+
+ /**
+ * Called after each navigation to indicate that the old page is no longer
+ * being rendered. Note this is not ordered with respect to onFirstContentfulPaint.
+ * @param newNavigationUri Uri of the new navigation.
+ * @since 85
+ */
+ public void onOldPageNoLongerRendered(@NonNull Uri newNavigationUri) {}
}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java
index 6267141c69f..d36650ff973 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java
@@ -69,6 +69,9 @@ public class NavigationController {
/**
* Navigates to the previous navigation.
*
+ * Note: this may go back more than a single navigation entry, see {@link
+ * isNavigationEntrySkippable} for more details.
+ *
* @throws IndexOutOfBoundsException If {@link #canGoBack} returns false.
*/
public void goBack() throws IndexOutOfBoundsException {
@@ -103,6 +106,9 @@ public class NavigationController {
/**
* Returns true if there is a navigation before the current one.
*
+ * Note: this may return false even if the current index is not 0, see {@link
+ * isNavigationEntrySkippable} for more details.
+ *
* @return Whether there is a navigation before the current one.
*/
public boolean canGoBack() {
@@ -212,6 +218,7 @@ public class NavigationController {
@NonNull
public Uri getNavigationEntryDisplayUri(int index) throws IndexOutOfBoundsException {
ThreadCheck.ensureOnUiThread();
+ checkNavigationIndex(index);
try {
return Uri.parse(mNavigationController.getNavigationEntryDisplayUri(index));
} catch (RemoteException e) {
@@ -219,12 +226,6 @@ public class NavigationController {
}
}
- private void checkNavigationIndex(int index) throws IndexOutOfBoundsException {
- if (index < 0 || index >= getNavigationListSize()) {
- throw new IndexOutOfBoundsException();
- }
- }
-
/**
* Returns the title of the navigation entry at the supplied index.
*
@@ -246,6 +247,28 @@ public class NavigationController {
}
}
+ /**
+ * Returns whether this entry will be skipped on a call to {@link goBack} or {@link goForward}.
+ * This will be true for certain navigations, such as certain client side redirects and
+ * history.pushState navigations done without user interaction.
+ *
+ * @throws IndexOutOfBoundsException If index is not between 0 and {@link
+ * getNavigationListCurrentIndex}.
+ * @since 85
+ */
+ public boolean isNavigationEntrySkippable(int index) throws IndexOutOfBoundsException {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ checkNavigationIndex(index);
+ try {
+ return mNavigationController.isNavigationEntrySkippable(index);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
public void registerNavigationCallback(@NonNull NavigationCallback callback) {
ThreadCheck.ensureOnUiThread();
mCallbacks.addObserver(callback);
@@ -256,6 +279,12 @@ public class NavigationController {
mCallbacks.removeObserver(callback);
}
+ private void checkNavigationIndex(int index) throws IndexOutOfBoundsException {
+ if (index < 0 || index >= getNavigationListSize()) {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
private final class NavigationControllerClientImpl extends INavigationControllerClient.Stub {
@Override
public IClientNavigation createClientNavigation(INavigation navigationImpl) {
@@ -326,5 +355,13 @@ public class NavigationController {
callback.onFirstContentfulPaint();
}
}
+
+ @Override
+ public void onOldPageNoLongerRendered(String uri) {
+ StrictModeWorkaround.apply();
+ for (NavigationCallback callback : mCallbacks) {
+ callback.onOldPageNoLongerRendered(Uri.parse(uri));
+ }
+ }
}
}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java b/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java
index 5d0eab7d54d..ed2071ea780 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java
@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
/**
* Profile holds state (typically on disk) needed for browsing. Create a
@@ -117,13 +118,13 @@ public class Profile {
}
/**
-  * Delete all profile data stored on disk. There are a number of edge cases with deleting
-  * profile data:
-  * * This method will throw an exception if there are any existing usage of this Profile. For
-  *   example, all BrowserFragment belonging to this profile must be destroyed.
-  * * This object is considered destroyed after this method returns. Calling any other method
-  *   after will throw exceptions.
-  * * Creating a new profile of the same name before doneCallback runs will throw an exception.
+ * Delete all profile data stored on disk. There are a number of edge cases with deleting
+ * profile data:
+ * * This method will throw an exception if there are any existing usage of this Profile. For
+ * example, all BrowserFragment belonging to this profile must be destroyed.
+ * * This object is considered destroyed after this method returns. Calling any other method
+ * after will throw exceptions.
+ * * Creating a new profile of the same name before doneCallback runs will throw an exception.
* @since 82
*/
public void destroyAndDeleteDataFromDisk(@Nullable Runnable completionCallback) {
@@ -254,6 +255,88 @@ public class Profile {
}
}
+ /**
+ * Asynchronously fetches the set of known Browser persistence-ids. See
+ * {@link WebLayer#createBrowserFragment} for details on the persistence-id.
+ *
+ * @param callback The callback that is supplied the set of ids.
+ *
+ * @throws IllegalStateException If called on an in memory profile.
+ *
+ * @since 85
+ */
+ public void getBrowserPersistenceIds(@NonNull Callback<Set<String>> callback) {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ if (mName.isEmpty()) {
+ throw new IllegalStateException(
+ "getBrowserPersistenceIds() is not applicable to in-memory profiles");
+ }
+ try {
+ mImpl.getBrowserPersistenceIds(
+ ObjectWrapper.wrap((ValueCallback<Set<String>>) callback::onResult));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Asynchronously removes the storage associated with the set of Browser persistence-ids. This
+ * ignores ids actively in use. {@link doneCallback} is supplied the result of the operation. A
+ * value of true means all files were removed. A value of false indicates at least one of the
+ * files could not be removed.
+ *
+ * @param callback The callback that is supplied the result of the operation.
+ *
+ * @throws IllegalStateException If called on an in memory profile.
+ * @throws IllegalArgumentException if {@link ids} contains an empty/null string.
+ *
+ * @since 85
+ */
+ public void removeBrowserPersistenceStorage(
+ @NonNull Set<String> ids, @NonNull Callback<Boolean> callback) {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ if (mName.isEmpty()) {
+ throw new IllegalStateException(
+ "removetBrowserPersistenceStorage() is not applicable to in-memory profiles");
+ }
+ try {
+ mImpl.removeBrowserPersistenceStorage(ids.toArray(new String[ids.size()]),
+ ObjectWrapper.wrap((ValueCallback<Boolean>) callback::onResult));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * For cross-origin navigations, the implementation may leverage a separate OS process for
+ * stronger isolation. If an embedder knows that a cross-origin navigation is likely starting
+ * soon, they can call this method as a hint to the implementation to start a fresh OS process.
+ * A subsequent navigation may use this preinitialized process, improving performance. It is
+ * safe to call this multiple times or when it is not certain that the spare renderer will be
+ * used, although calling this too eagerly may reduce performance as unnecessary processes are
+ * created.
+ *
+ * @since 85
+ */
+ public void prepareForPossibleCrossOriginNavigation() {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+
+ try {
+ mImpl.prepareForPossibleCrossOriginNavigation();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
static final class DownloadCallbackClientImpl extends IDownloadCallbackClient.Stub {
private final DownloadCallback mCallback;
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java b/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java
new file mode 100644
index 00000000000..b99949fe402
--- /dev/null
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java
@@ -0,0 +1,34 @@
+// 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;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+@IntDef({ScrollNotificationType.DIRECTION_CHANGED_UP,
+ ScrollNotificationType.DIRECTION_CHANGED_DOWN})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ScrollNotificationType {
+ /**
+ * This is the direction toward vertical scroll offset 0. Note direction change notification
+ * is sent on direction change. If there are two consecutive scrolls in the same direction,
+ * the second scroll will not generate a direction change notification. Also the notification
+ * is sent as a result of scroll change; this means for touch scrolls, this is sent (if there
+ * is a direction change) on the first touch move, not touch down.
+ */
+ int DIRECTION_CHANGED_UP =
+ org.chromium.weblayer_private.interfaces.ScrollNotificationType.DIRECTION_CHANGED_UP;
+
+ /**
+ * This is the direction away from vertical scroll offset 0. See notes on DIRECTION_CHANGED_UP.
+ */
+ int DIRECTION_CHANGED_DOWN =
+ org.chromium.weblayer_private.interfaces.ScrollNotificationType.DIRECTION_CHANGED_DOWN;
+}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java b/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java
index c5e800e8654..3fbc62492ff 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java
@@ -12,7 +12,9 @@ import java.lang.annotation.RetentionPolicy;
/**
* @hide
*/
-@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 {
/**
@@ -21,4 +23,39 @@ public @interface SettingType {
*/
int BASIC_SAFE_BROWSING_ENABLED =
org.chromium.weblayer_private.interfaces.SettingType.BASIC_SAFE_BROWSING_ENABLED;
+ /**
+ * Allows the embedder to enable URL-Keyed Metrics. Disabled by default.
+ */
+ int UKM_ENABLED = org.chromium.weblayer_private.interfaces.SettingType.UKM_ENABLED;
+
+ /**
+ * Allows the embedder to set whether it wants to enable/disable the Extended Reporting
+ * functionality for Safe Browsing (SBER). This functionality helps improve security on the web
+ * for everyone. It sends URLs of some pages you visit, limited system information, and some
+ * page content to Google, to help discover new threats and protect everyone on the web.
+ *
+ * This setting is disabled by default, but can also be enabled by the user by checking a
+ * checkbox in the Safe Browsing interstitial which is displayed when the user encounters a
+ * dangerous web page. The setting persists on disk.
+ *
+ * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED
+ * is true).
+ *
+ * @since 85
+ */
+ int EXTENDED_REPORTING_SAFE_BROWSING_ENABLED =
+ org.chromium.weblayer_private.interfaces.SettingType
+ .EXTENDED_REPORTING_SAFE_BROWSING_ENABLED;
+
+ /**
+ * Allows the embedder to set whether it wants to enable/disable the Safe Browsing Real-time URL
+ * checks. This functionality is disabled by default.
+ *
+ * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED
+ * is true).
+ *
+ * @since 85
+ */
+ int REAL_TIME_SAFE_BROWSING_ENABLED =
+ org.chromium.weblayer_private.interfaces.SettingType.REAL_TIME_SAFE_BROWSING_ENABLED;
}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java b/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java
index a4ed8ae8009..bed3eee69cd 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -22,11 +22,14 @@ import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
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.IWebMessageReplyProxy;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -344,6 +347,292 @@ public class Tab {
}
}
+ /**
+ * Set arbitrary data on the tab. This will be saved and restored with the browser, so it is
+ * important to keep this data as small as possible.
+ *
+ * @param data The data to set, must be smaller than 4K when serialized. A snapshot of this data
+ * is taken, so any changes to the passed in object after this call will not be reflected.
+ *
+ * @throws IllegalArgumentException if the serialzed size of the data exceeds 4K.
+ *
+ * @since 85
+ */
+ public void setData(@NonNull Map<String, String> data) {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ if (!mImpl.setData(data)) {
+ throw new IllegalArgumentException("Data given to Tab.setData() was too large.");
+ }
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Get arbitrary data set on the tab with setData().
+ *
+ * @return the data or an empty map if no data was set.
+ * @since 85
+ */
+ @NonNull
+ public Map<String, String> getData() {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return (Map<String, String>) mImpl.getData();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Adds a WebMessageCallback and injects a JavaScript object into each frame that the
+ * WebMessageCallback will listen on.
+ *
+ * <p>
+ * The injected JavaScript object will be named {@code jsObjectName} in the global scope. This
+ * will inject the JavaScript object in any frame whose origin matches {@code
+ * allowedOriginRules} for every navigation after this call, and the JavaScript object will be
+ * available immediately when the page begins to load.
+ *
+ * <p>
+ * Each {@code allowedOriginRules} entry must follow the format {@code SCHEME "://" [
+ * HOSTNAME_PATTERN [ ":" PORT ] ]}, each part is explained in the below table:
+ *
+ * <table>
+ * <col width="25%">
+ * <tr><th>Rule</th><th>Description</th><th>Example</th></tr>
+ *
+ * <tr>
+ * <td>http/https with hostname</td>
+ * <td>{@code SCHEME} is http or https; {@code HOSTNAME_PATTERN} is a regular hostname; {@code
+ * PORT} is optional, when not present, the rule will match port {@code 80} for http and port
+ * {@code 443} for https.</td>
+ * <td><ul>
+ * <li>{@code https://foobar.com:8080} - Matches https:// URL on port 8080, whose normalized
+ * host is foobar.com.</li>
+ * <li>{@code https://www.example.com} - Matches https:// URL on port 443, whose normalized host
+ * is www.example.com.</li>
+ * </ul></td>
+ * </tr>
+ *
+ * <tr>
+ * <td>http/https with pattern matching</td>
+ * <td>{@code SCHEME} is http or https; {@code HOSTNAME_PATTERN} is a sub-domain matching
+ * pattern with a leading {@code *.}; {@code PORT} is optional, when not present, the rule will
+ * match port {@code 80} for http and port {@code 443} for https.</td>
+ *
+ * <td><ul>
+ * <li>{@code https://*.example.com} - Matches https://calendar.example.com and
+ * https://foo.bar.example.com but not https://example.com.</li>
+ * <li>{@code https://*.example.com:8080} - Matches https://calendar.example.com:8080</li>
+ * </ul></td>
+ * </tr>
+ *
+ * <tr>
+ * <td>http/https with IP literal</td>
+ * <td>{@code SCHEME} is https or https; {@code HOSTNAME_PATTERN} is IP literal; {@code PORT} is
+ * optional, when not present, the rule will match port {@code 80} for http and port {@code 443}
+ * for https.</td>
+ *
+ * <td><ul>
+ * <li>{@code https://127.0.0.1} - Matches https:// URL on port 443, whose IPv4 address is
+ * 127.0.0.1</li>
+ * <li>{@code https://[::1]} or {@code https://[0:0::1]}- Matches any URL to the IPv6 loopback
+ * address with port 443.</li>
+ * <li>{@code https://[::1]:99} - Matches any https:// URL to the IPv6 loopback on port 99.</li>
+ * </ul></td>
+ * </tr>
+ *
+ * <tr>
+ * <td>Custom scheme</td>
+ * <td>{@code SCHEME} is a custom scheme; {@code HOSTNAME_PATTERN} and {@code PORT} must not be
+ * present.</td>
+ * <td><ul>
+ * <li>{@code my-app-scheme://} - Matches any my-app-scheme:// URL.</li>
+ * </ul></td>
+ * </tr>
+ *
+ * <tr><td>{@code *}</td>
+ * <td>Wildcard rule, matches any origin.</td>
+ * <td><ul><li>{@code *}</li></ul></td>
+ * </table>
+ *
+ * <p>
+ * Note that this is a powerful API, as the JavaScript object will be injected when the frame's
+ * origin matches any one of the allowed origins. The HTTPS scheme is strongly recommended for
+ * security; allowing HTTP origins exposes the injected object to any potential network-based
+ * attackers. If a wildcard {@code "*"} is provided, it will inject the JavaScript object to all
+ * frames. A wildcard should only be used if the app wants <b>any</b> third party web page to be
+ * able to use the injected object. When using a wildcard, the app must treat received messages
+ * as untrustworthy and validate any data carefully.
+ *
+ * <p>
+ * This method can be called multiple times to inject multiple JavaScript objects.
+ *
+ * <p>
+ * Let's say the injected JavaScript object is named {@code myObject}. We will have following
+ * methods on that object once it is available to use:
+ * <pre class="prettyprint">
+ * // message needs to be a JavaScript String.
+ * myObject.postMessage(message)
+ *
+ * // To receive the message posted from the app side. event has a "data" property, which is the
+ * // message string from the app side.
+ * myObject.onmessage(event)
+ *
+ * // To be compatible with DOM EventTarget's addEventListener, it accepts type and listener
+ * // parameters, where type can be only "message" type and listener can only be a JavaScript
+ * // function for myObject. An event object will be passed to listener with a "data" property,
+ * // which is the message string from the app side.
+ * myObject.addEventListener(type, listener)
+ *
+ * // To be compatible with DOM EventTarget's removeEventListener, it accepts type and listener
+ * // parameters, where type can be only "message" type and listener can only be a JavaScript
+ * // function for myObject.
+ * myObject.removeEventListener(type, listener)
+ * </pre>
+ *
+ * <p>
+ * We start the communication between JavaScript and the app from the JavaScript side. In order
+ * to send message from the app to JavaScript, it needs to post a message from JavaScript first,
+ * so the app will have a {@link WebMessageReplyProxy} object to respond. Example:
+ * <pre class="prettyprint">
+ * // Web page (in JavaScript)
+ * myObject.onmessage = function(event) {
+ * // prints "Got it!" when we receive the app's response.
+ * console.log(event.data);
+ * }
+ * myObject.postMessage("I'm ready!");
+ *
+ * // App (in Java)
+ * WebMessageCallback callback = new WebMessageCallback() {
+ * &#064;Override
+ * public void onWebMessageReceived(WebMessageReplyProxy proxy,
+ * WebMessage message) {
+ * // do something about view, message, sourceOrigin and isMainFrame.
+ * proxy.postMessage(new WebMessage("Got it!"));
+ * }
+ * };
+ * tab.registerWebMessageCallback(callback, "myObject", rules);
+ * </pre>
+ *
+ * @param callback The WebMessageCallback to notify of messages.
+ * @param jsObjectName The name to give the injected JavaScript object.
+ * @param allowedOrigins The set of allowed origins.
+ *
+ * @throws IllegalArgumentException if jsObjectName or allowedOrigins is invalid.
+ *
+ * @since 85
+ */
+ public void registerWebMessageCallback(@NonNull WebMessageCallback callback,
+ @NonNull String jsObjectName, @NonNull List<String> allowedOrigins) {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ mImpl.registerWebMessageCallback(
+ jsObjectName, allowedOrigins, new WebMessageCallbackClientImpl(callback));
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Removes the JavaScript object previously registered by way of registerWebMessageCallback.
+ * This impacts future navigations (not any already loaded navigations).
+ *
+ * @param jsObjectName Name of the JavaScript object.
+ * @since 85
+ */
+ public void unregisterWebMessageCallback(@NonNull String jsObjectName) {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ mImpl.unregisterWebMessageCallback(jsObjectName);
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Returns true if the content displayed in this tab can be translated.
+ *
+ * @since 85
+ */
+ public boolean canTranslate() {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mImpl.canTranslate();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ /**
+ * Shows the UI which allows the user to translate the content displayed in this tab.
+ *
+ * @since 85
+ */
+ public void showTranslateUi() {
+ ThreadCheck.ensureOnUiThread();
+ if (WebLayer.getSupportedMajorVersionInternal() < 85) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ mImpl.showTranslateUi();
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+
+ private static final class WebMessageCallbackClientImpl extends IWebMessageCallbackClient.Stub {
+ private final WebMessageCallback mCallback;
+ // Maps from id of IWebMessageReplyProxy to WebMessageReplyProxy. This is done to avoid AIDL
+ // creating an implementation of IWebMessageReplyProxy every time onPostMessage() is called.
+ private final Map<Integer, WebMessageReplyProxy> mProxyIdToProxy =
+ new HashMap<Integer, WebMessageReplyProxy>();
+
+ private WebMessageCallbackClientImpl(WebMessageCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onNewReplyProxy(IWebMessageReplyProxy proxy, int proxyId, boolean isMainFrame,
+ String sourceOrigin) {
+ StrictModeWorkaround.apply();
+ assert mProxyIdToProxy.get(proxyId) == null;
+ mProxyIdToProxy.put(
+ proxyId, new WebMessageReplyProxy(proxy, isMainFrame, sourceOrigin));
+ }
+
+ @Override
+ public void onPostMessage(int proxyId, String string) {
+ StrictModeWorkaround.apply();
+ assert mProxyIdToProxy.get(proxyId) != null;
+ mCallback.onWebMessageReceived(mProxyIdToProxy.get(proxyId), new WebMessage(string));
+ }
+
+ @Override
+ public void onReplyProxyDestroyed(int proxyId) {
+ StrictModeWorkaround.apply();
+ assert mProxyIdToProxy.get(proxyId) != null;
+ mProxyIdToProxy.remove(proxyId);
+ }
+ }
+
private final class TabClientImpl extends ITabClient.Stub {
@Override
public void visibleUriChanged(String uriString) {
@@ -437,6 +726,23 @@ public class Tab {
callback.bringTabToFront();
}
}
+
+ @Override
+ public void onBackgroundColorChanged(int color) {
+ StrictModeWorkaround.apply();
+ for (TabCallback callback : mCallbacks) {
+ callback.onBackgroundColorChanged(color);
+ }
+ }
+
+ @Override
+ public void onScrollNotification(
+ @ScrollNotificationType int notificationType, float currentScrollRatio) {
+ StrictModeWorkaround.apply();
+ for (TabCallback callback : mCallbacks) {
+ callback.onScrollNotification(notificationType, currentScrollRatio);
+ }
+ }
}
private static final class ErrorPageCallbackClientImpl extends IErrorPageCallbackClient.Stub {
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java
index 007a7160453..5b1ecf4298c 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java
@@ -54,4 +54,28 @@ public abstract class TabCallback {
* containing Activity, and the task to be foregrounded.
*/
public void bringTabToFront() {}
+
+ /**
+ * Called when then background color of the page changes. The background color typically comes
+ * from css background-color, but heuristics and blending may be used depending upon the page.
+ * This is mostly useful for filling in gaps around the web page during resize, but it will
+ * not necessarily match the full background of the page.
+ * @param color The new ARGB color of the page background.
+ * @since 85
+ */
+ public void onBackgroundColorChanged(int color) {}
+
+ /**
+ * Notification for scroll of the root of the web page. This is generally sent as a result of
+ * displaying web page. See ScrollNotificationType for more details. ScrollNotificationType is
+ * meant to be extensible and new types may be added in the future. Embedder should take care
+ * to allow unknown values.
+ * @param notificationType type of notification. See ScrollNotificationType for more details.
+ * @param currentScrollRatio value in [0, 1] indicating the current scroll ratio. For example
+ * a web page that is 200 pixels, has a viewport of height 50 pixels
+ * and a scroll offset of 50 pixels will have a ratio of 0.5.
+ * @since 85
+ */
+ public void onScrollNotification(
+ @ScrollNotificationType int notificationType, float currentScrollRatio) {}
}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index 50eab9dcb31..b47b68ec6cf 100644
--- a/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -32,6 +32,7 @@ import org.chromium.weblayer_private.interfaces.IWebLayerClient;
import org.chromium.weblayer_private.interfaces.IWebLayerFactory;
import org.chromium.weblayer_private.interfaces.ObjectWrapper;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
+import org.chromium.weblayer_private.interfaces.WebLayerVersionConstants;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -179,6 +180,14 @@ public class WebLayer {
return mImpl;
}
+ static WebLayer getLoadedWebLayer(@NonNull Context appContext)
+ throws UnsupportedVersionException {
+ ThreadCheck.ensureOnUiThread();
+ appContext = appContext.getApplicationContext();
+ checkAvailable(appContext);
+ return getWebLayerLoader(appContext).getLoadedWebLayer();
+ }
+
/**
* Returns the supported version. Using any functions defined in a newer version than
* returned by {@link getSupportedMajorVersion} result in throwing an
@@ -270,17 +279,20 @@ public class WebLayer {
}
Class factoryClass = remoteClassLoader.loadClass(
"org.chromium.weblayer_private.WebLayerFactoryImpl");
- // NOTE: the 20 comes from the previous scheme of incrementing versioning. It must
- // remain at 20 for Chrome version 79.
- // TODO(https://crbug.com/1031830): change 20 to -1 when 83 goes to stable.
mFactory = IWebLayerFactory.Stub.asInterface(
(IBinder) factoryClass
.getMethod("create", String.class, int.class, int.class)
.invoke(null, WebLayerClientVersionConstants.PRODUCT_VERSION,
- WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION, 20));
+ WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION, -1));
available = mFactory.isClientSupported();
majorVersion = mFactory.getImplementationMajorVersion();
version = mFactory.getImplementationVersion();
+ // See comment in WebLayerFactoryImpl.isClientSupported() for details on this.
+ if (available
+ && WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION > majorVersion) {
+ available = WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION - majorVersion
+ <= WebLayerVersionConstants.MAX_SKEW;
+ }
} catch (Exception e) {
Log.e(TAG, "Unable to create WebLayerFactory", e);
}
@@ -360,6 +372,10 @@ public class WebLayer {
}
}
+ WebLayer getLoadedWebLayer() {
+ return mWebLayer;
+ }
+
@Nullable
private IWebLayer getIWebLayer(@NonNull Context appContext) {
if (mIWebLayer != null) return mIWebLayer;
@@ -668,5 +684,18 @@ public class WebLayer {
// client library because it's referenced in the manifest.
return new Intent(WebLayer.getAppContext(), BroadcastReceiver.class);
}
+
+ @Override
+ public Intent createMediaSessionServiceIntent() {
+ StrictModeWorkaround.apply();
+ return new Intent(WebLayer.getAppContext(), MediaSessionService.class);
+ }
+
+ @Override
+ public int getMediaSessionNotificationId() {
+ StrictModeWorkaround.apply();
+ // The id is part of the public library to avoid conflicts.
+ return R.id.weblayer_media_session_notification;
+ }
}
}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java
new file mode 100644
index 00000000000..7fd27bd2bdc
--- /dev/null
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java
@@ -0,0 +1,34 @@
+// 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;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Used when sending and receiving messages to a page.
+ *
+ * @since 85
+ */
+public class WebMessage {
+ private final String mContents;
+
+ /**
+ * Creates a message with the specified contents.
+ *
+ * @param message Contents of the message.
+ */
+ public WebMessage(@NonNull String message) {
+ mContents = message;
+ }
+
+ /**
+ * Returns the contents of the message.
+ *
+ * @return The contents of the message.
+ */
+ public @NonNull String getContents() {
+ return mContents;
+ }
+}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java
new file mode 100644
index 00000000000..bf35f5a4b5d
--- /dev/null
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java
@@ -0,0 +1,24 @@
+// 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;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Receives messages from a JavaScript object that was created by calling {@link
+ * Tab#registerWebMessageCallback().
+ *
+ * @since 85
+ */
+public abstract class WebMessageCallback {
+ /**
+ * Called when a message is received from the page.
+ *
+ * @param replyProxy An object that may be used to post a message back to the page.
+ * @param message The message from the page.
+ */
+ public void onWebMessageReceived(
+ @NonNull WebMessageReplyProxy replyProxy, @NonNull WebMessage message) {}
+}
diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java
new file mode 100644
index 00000000000..de9df58d879
--- /dev/null
+++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java
@@ -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.
+
+package org.chromium.weblayer;
+
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.IWebMessageReplyProxy;
+
+/**
+ * Used to post a message to a page. WebMessageReplyProxy is created when a page posts a message to
+ * the JavaScript object that was created by way of {@link Tab#registerWebMessageCallback}.
+ *
+ * @since 85
+ */
+public class WebMessageReplyProxy {
+ private final IWebMessageReplyProxy mIReplyProxy;
+ private final boolean mIsMainFrame;
+ private final String mSourceOrigin;
+
+ // Constructor for test mocking.
+ protected WebMessageReplyProxy() {
+ this(null, false, "");
+ }
+
+ WebMessageReplyProxy(
+ IWebMessageReplyProxy iReplyProxy, boolean isMainFrame, String sourceOrigin) {
+ mIReplyProxy = iReplyProxy;
+ mIsMainFrame = isMainFrame;
+ mSourceOrigin = sourceOrigin;
+ }
+
+ /**
+ * Returns true if the connection is to the main frame.
+ *
+ * @return True if the connection is to the main frame.
+ */
+ public boolean isMainFrame() {
+ ThreadCheck.ensureOnUiThread();
+ return mIsMainFrame;
+ }
+
+ /**
+ * Returns the origin of the page.
+ *
+ * @return the origin of the page.
+ */
+ public @NonNull String getSourceOrigin() {
+ ThreadCheck.ensureOnUiThread();
+ return mSourceOrigin;
+ }
+
+ /**
+ * Posts a message to the page.
+ *
+ * @param message The message to send to the page.
+ */
+ public void postMessage(@NonNull WebMessage message) {
+ ThreadCheck.ensureOnUiThread();
+ try {
+ mIReplyProxy.postMessage(message.getContents());
+ } catch (RemoteException e) {
+ throw new APICallException(e);
+ }
+ }
+}
diff --git a/chromium/weblayer/public/java/res/values-night/colors.xml b/chromium/weblayer/public/java/res/values-night/colors.xml
new file mode 100644
index 00000000000..3846e456aee
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values-night/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+ <color name="weblayer_default_bg_color">@color/weblayer_default_bg_color_dark</color>
+ <color name="weblayer_bottom_system_nav_color">@android:color/black</color>
+ <color name="weblayer_bottom_system_nav_divider_color">@android:color/black</color>
+</resources>
diff --git a/chromium/weblayer/public/java/res/values-night/values.xml b/chromium/weblayer/public/java/res/values-night/values.xml
new file mode 100644
index 00000000000..5009ad51a2a
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values-night/values.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Status and navigation bar icon styling. -->
+ <bool name="weblayer_window_light_status_bar">false</bool>
+ <bool name="weblayer_window_light_navigation_bar">false</bool>
+</resources>
diff --git a/chromium/weblayer/public/java/res/values-v27/styles.xml b/chromium/weblayer/public/java/res/values-v27/styles.xml
new file mode 100644
index 00000000000..0ffe78b5f62
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values-v27/styles.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+ <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings">
+ <item name="android:navigationBarColor">@color/weblayer_bottom_system_nav_color</item>
+ <item name="android:navigationBarDividerColor">@color/weblayer_bottom_system_nav_divider_color</item>
+ <item name="android:windowLightNavigationBar">@bool/weblayer_window_light_navigation_bar</item>
+ </style>
+</resources>
diff --git a/chromium/weblayer/public/java/res/values-v28/styles.xml b/chromium/weblayer/public/java/res/values-v28/styles.xml
new file mode 100644
index 00000000000..0a21de0a4eb
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values-v28/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+ <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings">
+ <item name="android:navigationBarColor">@color/weblayer_bottom_system_nav_color</item>
+ <item name="android:navigationBarDividerColor">@color/weblayer_bottom_system_nav_divider_color</item>
+ <item name="android:windowLightNavigationBar">@bool/weblayer_window_light_navigation_bar</item>
+
+ <!-- The windowLightStatusBar attribute was added in API 23, but we
+ avoid using it via XML prior to 28 due to: https://crbug.com/884144
+ and https://crbug.com/1014844 -->
+ <item name="android:statusBarColor">@color/weblayer_default_bg_color</item>
+ <item name="android:windowLightStatusBar">@bool/weblayer_window_light_status_bar</item>
+ </style>
+</resources>
diff --git a/chromium/weblayer/public/java/res/values/colors.xml b/chromium/weblayer/public/java/res/values/colors.xml
new file mode 100644
index 00000000000..bb86aec4897
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values/colors.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="UnusedResources">
+ <color name="weblayer_default_bg_color">@color/weblayer_default_bg_color_light</color>
+ <color name="weblayer_default_bg_color_light">@android:color/white</color>
+ <color name="weblayer_default_bg_color_dark">@color/weblayer_modern_grey_900</color>
+
+ <color name="weblayer_bottom_system_nav_color">@android:color/white</color>
+ <color name="weblayer_bottom_system_nav_divider_color">@color/weblayer_black_alpha_12</color>
+
+ <color name="weblayer_black_alpha_12">#1F000000</color>
+ <color name="weblayer_modern_grey_900">#202124</color>
+</resources>
diff --git a/chromium/weblayer/public/java/res/values/ids.xml b/chromium/weblayer/public/java/res/values/ids.xml
new file mode 100644
index 00000000000..300c7e62a50
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values/ids.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+ <item type="id" name="weblayer_media_session_notification" />
+</resources>
diff --git a/chromium/weblayer/public/java/res/values/styles.xml b/chromium/weblayer/public/java/res/values/styles.xml
index 478e0f2bbcd..143124cc560 100644
--- a/chromium/weblayer/public/java/res/values/styles.xml
+++ b/chromium/weblayer/public/java/res/values/styles.xml
@@ -4,21 +4,18 @@
found in the LICENSE file. -->
<resources xmlns:tools="http://schemas.android.com/tools">
- <style name="Theme.WebLayer" parent="Theme.BrowserUI">
+ <style name="Base.Theme.WebLayer.SiteSettings" parent="Theme.AppCompat.DayNight">
<!-- Window Properties -->
- <item name="android:windowBackground">@color/default_bg_color</item>
+ <item name="android:windowBackground">@color/weblayer_default_bg_color</item>
<!-- Action bar color -->
- <item name="colorPrimary">@color/default_bg_color</item>
+ <item name="colorPrimary">@color/weblayer_default_bg_color</item>
<!-- Status bar color -->
+ <item name="colorPrimaryDark">@android:color/black</item>
<item name="android:statusBarColor" tools:targetApi="21">@android:color/black</item>
<item name="android:windowLightStatusBar" tools:targetApi="23">false</item>
- <item name="colorPrimaryDark">@android:color/black</item>
</style>
- <style name="Theme.WebLayer.SiteSettings">
- <item name="windowActionBar">true</item>
- <item name="windowNoTitle">false</item>
- </style>
+ <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings" />
</resources>
diff --git a/chromium/weblayer/public/java/res/values/values.xml b/chromium/weblayer/public/java/res/values/values.xml
new file mode 100644
index 00000000000..a04c1d9a0ea
--- /dev/null
+++ b/chromium/weblayer/public/java/res/values/values.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Status and navigation bar icon styling. -->
+ <bool name="weblayer_window_light_status_bar">true</bool>
+ <bool name="weblayer_window_light_navigation_bar">true</bool>
+</resources>
diff --git a/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java b/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java
index 35343813c9d..605037dd909 100644
--- a/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java
+++ b/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java
@@ -4,7 +4,7 @@
package org.chromium.weblayer;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Test;
diff --git a/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java b/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
index ceca8dc15a9..998f0907ec4 100644
--- a/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
+++ b/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
@@ -7,9 +7,10 @@ package org.chromium.weblayer;
import android.content.Context;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/chromium/weblayer/public/javatestutil/BUILD.gn b/chromium/weblayer/public/javatestutil/BUILD.gn
index 48b69a71639..5fd626da668 100644
--- a/chromium/weblayer/public/javatestutil/BUILD.gn
+++ b/chromium/weblayer/public/javatestutil/BUILD.gn
@@ -12,6 +12,7 @@ android_library("test_java") {
deps = [
"//base:base_java_test_support",
"//third_party/junit:junit",
+ "//weblayer/browser/java:interfaces_java",
"//weblayer/browser/java:test_java",
"//weblayer/public/java",
]
diff --git a/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java b/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java
index 370103f4fab..1623a9e7906 100644
--- a/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java
+++ b/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java
@@ -9,10 +9,12 @@ import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.AndroidRuntimeException;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
import org.chromium.weblayer_private.test_interfaces.ITestWebLayer;
/**
@@ -75,4 +77,41 @@ public final class TestWebLayer {
public void setSystemLocationSettingEnabled(boolean enabled) throws RemoteException {
mITestWebLayer.setSystemLocationSettingEnabled(enabled);
}
+
+ // Runs |runnable| when cc::RenderFrameMetadata's |top_controls_height| and
+ // |bottom_controls_height| matches the supplied values. |runnable| may be run synchronously.
+ public void waitForBrowserControlsMetadataState(Tab tab, int top, int bottom, Runnable runnable)
+ throws RemoteException {
+ mITestWebLayer.waitForBrowserControlsMetadataState(
+ tab.getITab(), top, bottom, ObjectWrapper.wrap(runnable));
+ }
+
+ public void setAccessibilityEnabled(boolean enabled) throws RemoteException {
+ mITestWebLayer.setAccessibilityEnabled(enabled);
+ }
+
+ public boolean canBrowserControlsScroll(Tab tab) throws RemoteException {
+ return mITestWebLayer.canBrowserControlsScroll(tab.getITab());
+ }
+
+ public void addInfoBar(Tab tab, Runnable runnable) throws RemoteException {
+ mITestWebLayer.addInfoBar(tab.getITab(), ObjectWrapper.wrap(runnable));
+ }
+
+ public View getInfoBarContainerView(Tab tab) throws RemoteException {
+ return (View) ObjectWrapper.unwrap(
+ mITestWebLayer.getInfoBarContainerView(tab.getITab()), View.class);
+ }
+
+ public void setIgnoreMissingKeyForTranslateManager(boolean ignore) throws RemoteException {
+ mITestWebLayer.setIgnoreMissingKeyForTranslateManager(ignore);
+ }
+
+ public void forceNetworkConnectivityState(boolean networkAvailable) throws RemoteException {
+ mITestWebLayer.forceNetworkConnectivityState(networkAvailable);
+ }
+
+ public boolean canInfoBarContainerScroll(Tab tab) throws RemoteException {
+ return mITestWebLayer.canInfoBarContainerScroll(tab.getITab());
+ }
}
diff --git a/chromium/weblayer/public/js_communication/web_message.cc b/chromium/weblayer/public/js_communication/web_message.cc
new file mode 100644
index 00000000000..ebdcd6632d0
--- /dev/null
+++ b/chromium/weblayer/public/js_communication/web_message.cc
@@ -0,0 +1,13 @@
+// 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"
+
+namespace weblayer {
+
+WebMessage::WebMessage() = default;
+
+WebMessage::~WebMessage() = default;
+
+} // namespace weblayer
diff --git a/chromium/weblayer/public/js_communication/web_message.h b/chromium/weblayer/public/js_communication/web_message.h
new file mode 100644
index 00000000000..169cbe1da0c
--- /dev/null
+++ b/chromium/weblayer/public/js_communication/web_message.h
@@ -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.
+
+#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_
+#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_
+
+#include "base/strings/string16.h"
+
+namespace weblayer {
+
+struct WebMessage {
+ WebMessage();
+ ~WebMessage();
+
+ base::string16 message;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_
diff --git a/chromium/weblayer/public/js_communication/web_message_host.h b/chromium/weblayer/public/js_communication/web_message_host.h
new file mode 100644
index 00000000000..b81ab149abd
--- /dev/null
+++ b/chromium/weblayer/public/js_communication/web_message_host.h
@@ -0,0 +1,26 @@
+// 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_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_
+#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_
+
+#include <memory>
+
+namespace weblayer {
+
+struct WebMessage;
+
+// Represents the browser side of a WebMessage channel. See
+// WebMessageHostFactory for details.
+class WebMessageHost {
+ public:
+ virtual ~WebMessageHost() = default;
+
+ // Called when the page sends a message to the browser side.
+ virtual void OnPostMessage(std::unique_ptr<WebMessage> message) = 0;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_
diff --git a/chromium/weblayer/public/js_communication/web_message_host_factory.h b/chromium/weblayer/public/js_communication/web_message_host_factory.h
new file mode 100644
index 00000000000..2f754e15653
--- /dev/null
+++ b/chromium/weblayer/public/js_communication/web_message_host_factory.h
@@ -0,0 +1,35 @@
+// 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_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_
+#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_
+
+#include <memory>
+#include <string>
+
+namespace weblayer {
+
+class WebMessageHost;
+class WebMessageReplyProxy;
+
+// Creates a WebMessageHost in response to a page interacting with the object
+// registered by way of Tab::AddWebMessageHostFactory(). A WebMessageHost is
+// created for every page that matches the parameters of
+// AddWebMessageHostFactory().
+class WebMessageHostFactory {
+ public:
+ virtual ~WebMessageHostFactory() = default;
+
+ // The returned object is destroyed when the corresponding renderer has
+ // been destroyed. |proxy| may be used to send messages to the page and is
+ // valid for the life of the WebMessageHost.
+ virtual std::unique_ptr<WebMessageHost> CreateHost(
+ const std::string& origin_string,
+ bool is_main_frame,
+ WebMessageReplyProxy* proxy) = 0;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_
diff --git a/chromium/weblayer/public/js_communication/web_message_reply_proxy.h b/chromium/weblayer/public/js_communication/web_message_reply_proxy.h
new file mode 100644
index 00000000000..e7147096494
--- /dev/null
+++ b/chromium/weblayer/public/js_communication/web_message_reply_proxy.h
@@ -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.
+
+#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_
+#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_
+
+#include <memory>
+
+namespace weblayer {
+
+struct WebMessage;
+
+// Used to send messages to the page.
+class WebMessageReplyProxy {
+ public:
+ virtual void PostMessage(std::unique_ptr<WebMessage>) = 0;
+
+ protected:
+ virtual ~WebMessageReplyProxy() = default;
+};
+
+} // namespace weblayer
+
+#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_
diff --git a/chromium/weblayer/public/navigation.h b/chromium/weblayer/public/navigation.h
index 725962e4908..af69c1bbfe8 100644
--- a/chromium/weblayer/public/navigation.h
+++ b/chromium/weblayer/public/navigation.h
@@ -94,6 +94,11 @@ class Navigation {
// and redirect. When called during start, the header applies to both the
// start and redirect. |name| must be rfc 2616 compliant and |value| must
// not contain '\0', '\n' or '\r'.
+ //
+ // This function may be used to set the referer. If the referer is set in
+ // navigation start, it is reset during the redirect. In other words, if you
+ // need to set a referer that applies to redirects, then this must be called
+ // from NavigationRedirected().
virtual void SetRequestHeader(const std::string& name,
const std::string& value) = 0;
diff --git a/chromium/weblayer/public/navigation_controller.h b/chromium/weblayer/public/navigation_controller.h
index 4b042c19353..c584c5d1432 100644
--- a/chromium/weblayer/public/navigation_controller.h
+++ b/chromium/weblayer/public/navigation_controller.h
@@ -60,6 +60,11 @@ class NavigationController {
// Gets the page title of the given entry in the back/forward list, or an
// empty string if there is no navigation entry at that index.
virtual std::string GetNavigationEntryTitle(int index) = 0;
+
+ // Returns whether this entry will be skipped on a call to GoBack() or
+ // GoForward(). This will be true for navigations that were done without a
+ // user gesture, including both client side redirects and history.pushState.
+ virtual bool IsNavigationEntrySkippable(int index) = 0;
};
} // namespace weblayer
diff --git a/chromium/weblayer/public/navigation_observer.h b/chromium/weblayer/public/navigation_observer.h
index 6b0d2dda9eb..86567cb5255 100644
--- a/chromium/weblayer/public/navigation_observer.h
+++ b/chromium/weblayer/public/navigation_observer.h
@@ -5,6 +5,8 @@
#ifndef WEBLAYER_PUBLIC_NAVIGATION_OBSERVER_H_
#define WEBLAYER_PUBLIC_NAVIGATION_OBSERVER_H_
+class GURL;
+
namespace weblayer {
class Navigation;
@@ -95,6 +97,11 @@ class NavigationObserver {
// This is fired after each navigation has completed to indicate that the
// first paint after a non-empty layout has finished.
virtual void OnFirstContentfulPaint() {}
+
+ // Called after each navigation to indicate that the old page is no longer
+ // being rendered. Note this is not ordered with respect to
+ // OnFirstContentfulPaint.
+ virtual void OnOldPageNoLongerRendered(const GURL& url) {}
};
} // namespace weblayer
diff --git a/chromium/weblayer/public/new_tab_delegate.h b/chromium/weblayer/public/new_tab_delegate.h
index 2d131444df1..27291bf1b99 100644
--- a/chromium/weblayer/public/new_tab_delegate.h
+++ b/chromium/weblayer/public/new_tab_delegate.h
@@ -33,7 +33,9 @@ enum class NewTabType {
// in web terms, a new popup/window (and random other things).
class NewTabDelegate {
public:
- virtual void OnNewTab(std::unique_ptr<Tab> new_tab, NewTabType type) = 0;
+ // Called when a new tab is created by the browser. |new_tab| is owned by the
+ // browser.
+ virtual void OnNewTab(Tab* new_tab, NewTabType type) = 0;
// The page has requested a tab that was created by way of OnNewTab() to be
// closed. This is sent to the NewTabDelegate set on the page created by way
diff --git a/chromium/weblayer/public/profile.h b/chromium/weblayer/public/profile.h
index f878ed679c9..48f49c52758 100644
--- a/chromium/weblayer/public/profile.h
+++ b/chromium/weblayer/public/profile.h
@@ -5,9 +5,13 @@
#ifndef WEBLAYER_PUBLIC_PROFILE_H_
#define WEBLAYER_PUBLIC_PROFILE_H_
-#include <algorithm>
+#include <memory>
#include <string>
+#include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
+#include "base/time/time.h"
+
namespace base {
class FilePath;
}
@@ -23,8 +27,12 @@ enum class BrowsingDataType {
CACHE = 1,
};
+// Used for setting/getting profile related settings.
enum class SettingType {
BASIC_SAFE_BROWSING_ENABLED = 0,
+ UKM_ENABLED = 1,
+ EXTENDED_REPORTING_SAFE_BROWSING_ENABLED = 2,
+ REAL_TIME_SAFE_BROWSING_ENABLED = 3,
};
class Profile {
@@ -61,11 +69,35 @@ class Profile {
// Gets the cookie manager for this profile.
virtual CookieManager* GetCookieManager() = 0;
+ // Asynchronously fetches the set of known Browser persistence-ids. See
+ // Browser::PersistenceInfo for more details on persistence-ids.
+ virtual void GetBrowserPersistenceIds(
+ base::OnceCallback<void(base::flat_set<std::string>)> callback) = 0;
+
+ // Asynchronously removes the storage associated with the set of
+ // Browser persistence-ids. This ignores ids actively in use. |done_callback|
+ // is run with the result of the operation (on the main thread). A value of
+ // true means all files were removed. A value of false indicates at least one
+ // of the files could not be removed.
+ virtual void RemoveBrowserPersistenceStorage(
+ base::OnceCallback<void(bool)> done_callback,
+ base::flat_set<std::string> ids) = 0;
+
// Set the boolean value of the given setting type.
virtual void SetBooleanSetting(SettingType type, bool value) = 0;
// Get the boolean value of the given setting type.
virtual bool GetBooleanSetting(SettingType type) = 0;
+
+ // For cross-origin navigations, the implementation may leverage a separate OS
+ // process for stronger isolation. If an embedder knows that a cross-origin
+ // navigation is likely starting soon, they can call this method as a hint to
+ // the implementation to start a fresh OS process. A subsequent navigation may
+ // use this preinitialized process, improving performance. It is safe to call
+ // this multiple times or when it is not certain that the spare renderer will
+ // be used, although calling this too eagerly may reduce performance as
+ // unnecessary processes are created.
+ virtual void PrepareForPossibleCrossOriginNavigation() = 0;
};
} // namespace weblayer
diff --git a/chromium/weblayer/public/tab.h b/chromium/weblayer/public/tab.h
index eeec5ff218a..ccf25ff7fd6 100644
--- a/chromium/weblayer/public/tab.h
+++ b/chromium/weblayer/public/tab.h
@@ -6,7 +6,9 @@
#define WEBLAYER_PUBLIC_TAB_H_
#include <algorithm>
+#include <map>
#include <string>
+#include <vector>
#include "base/callback_forward.h"
#include "base/strings/string16.h"
@@ -27,19 +29,13 @@ class ErrorPageDelegate;
class FullscreenDelegate;
class NavigationController;
class NewTabDelegate;
-class Profile;
class TabObserver;
+class WebMessageHostFactory;
// Represents a tab that is navigable.
class Tab {
public:
- static std::unique_ptr<Tab> Create(Profile* profile);
-
-#if defined(OS_ANDROID)
- static Tab* GetLastTabForTesting();
-#endif
-
- virtual ~Tab() {}
+ virtual ~Tab() = default;
// Sets the ErrorPageDelegate. If none is set, a default action will be taken
// for any given interaction with an error page.
@@ -75,6 +71,40 @@ class Tab {
// Returns the tab's guid.
virtual const std::string& GetGuid() = 0;
+ // Allows the embedder to get and set arbitrary data on the tab. This will be
+ // saved and restored with the browser, so it is important to keep this data
+ // as small as possible.
+ virtual void SetData(const std::map<std::string, std::string>& data) = 0;
+ virtual const std::map<std::string, std::string>& GetData() = 0;
+
+ // Adds a new WebMessageHostFactory. For any urls that match
+ // |allowed_origin_rules| a JS object is registered using the name
+ // |js_object_name| (in the global namespace). Script may use the object to
+ // send and receive messages and is available at page load time.
+ //
+ // The page is responsible for initiating the connection. That is,
+ // WebMessageHostFactory::CreateHost() is called once the page posts a
+ // message to the JS object.
+ //
+ // |allowed_origin_rules| is a set of rules used to determine which pages
+ // this applies to. '*' may be used to match anything. If not '*' the format
+ // is 'scheme://host:port':
+ // . scheme: The scheme, which can not be empty or contain '*'.
+ // . host: The host to match against. Can not contain '/' and may start with
+ // '*.' to match against a specific subdomain.
+ // . port (optional): matches a specific port.
+ //
+ // Returns an empty string on success. On failure, the return string gives
+ // an error message.
+ virtual base::string16 AddWebMessageHostFactory(
+ std::unique_ptr<WebMessageHostFactory> factory,
+ const base::string16& js_object_name,
+ const std::vector<std::string>& allowed_origin_rules) = 0;
+
+ // Removes the WebMessageHostFactory registered under |js_object_name|.
+ virtual void RemoveWebMessageHostFactory(
+ const base::string16& js_object_name) = 0;
+
#if !defined(OS_ANDROID)
// TODO: this isn't a stable API, so use it now for expediency in the C++ API,
// but if we ever want to have backward or forward compatibility in C++ this
diff --git a/chromium/weblayer/renderer/DEPS b/chromium/weblayer/renderer/DEPS
index 2c8bba7e64b..0256937da2e 100644
--- a/chromium/weblayer/renderer/DEPS
+++ b/chromium/weblayer/renderer/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+components/cdm/renderer",
"+components/content_settings/renderer",
"+components/error_page/common",
+ "+components/grit",
"+components/page_load_metrics/renderer",
"+components/safe_browsing/content/common",
"+components/safe_browsing/content/renderer",
diff --git a/chromium/weblayer/renderer/content_renderer_client_impl.cc b/chromium/weblayer/renderer/content_renderer_client_impl.cc
index 0b4e7fbdb14..490e7e45d5f 100644
--- a/chromium/weblayer/renderer/content_renderer_client_impl.cc
+++ b/chromium/weblayer/renderer/content_renderer_client_impl.cc
@@ -10,9 +10,12 @@
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "components/content_settings/renderer/content_settings_agent_impl.h"
#include "components/error_page/common/error.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/js_injection/renderer/js_communication.h"
#include "components/page_load_metrics/renderer/metrics_render_frame_observer.h"
#include "content/public/renderer/render_thread.h"
#include "third_party/blink/public/platform/platform.h"
+#include "ui/base/resource/resource_bundle.h"
#include "weblayer/common/features.h"
#include "weblayer/renderer/error_page_helper.h"
#include "weblayer/renderer/weblayer_render_frame_observer.h"
@@ -25,6 +28,7 @@
#include "components/spellcheck/renderer/spellcheck_provider.h" // nogncheck
#include "content/public/renderer/render_thread.h"
#include "services/service_manager/public/cpp/local_interface_provider.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
#include "weblayer/renderer/url_loader_throttle_provider.h"
#endif
@@ -101,6 +105,19 @@ void ContentRendererClientImpl::RenderFrameCreated(
new SpellCheckProvider(render_frame, spellcheck_.get(),
local_interface_provider_.get());
#endif
+ new js_injection::JsCommunication(render_frame);
+}
+
+SkBitmap* ContentRendererClientImpl::GetSadPluginBitmap() {
+ return const_cast<SkBitmap*>(ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_SAD_PLUGIN)
+ .ToSkBitmap());
+}
+
+SkBitmap* ContentRendererClientImpl::GetSadWebViewBitmap() {
+ return const_cast<SkBitmap*>(ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_SAD_WEBVIEW)
+ .ToSkBitmap());
}
bool ContentRendererClientImpl::HasErrorPage(int http_status_code) {
@@ -160,4 +177,14 @@ void ContentRendererClientImpl::AddSupportedKeySystems(
#endif
}
+void ContentRendererClientImpl::
+ SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() {
+#if defined(OS_ANDROID)
+ // Web Share is experimental by default, and explicitly enabled on Android
+ // (for both Chrome and WebLayer).
+ blink::WebRuntimeFeatures::EnableWebShare(true);
+ blink::WebRuntimeFeatures::EnableWebShareV2(true);
+#endif
+}
+
} // namespace weblayer
diff --git a/chromium/weblayer/renderer/content_renderer_client_impl.h b/chromium/weblayer/renderer/content_renderer_client_impl.h
index 1d7d387aa57..b149a0f3e35 100644
--- a/chromium/weblayer/renderer/content_renderer_client_impl.h
+++ b/chromium/weblayer/renderer/content_renderer_client_impl.h
@@ -27,6 +27,8 @@ class ContentRendererClientImpl : public content::ContentRendererClient {
// content::ContentRendererClient:
void RenderThreadStarted() override;
void RenderFrameCreated(content::RenderFrame* render_frame) override;
+ SkBitmap* GetSadPluginBitmap() override;
+ SkBitmap* GetSadWebViewBitmap() override;
bool HasErrorPage(int http_status_code) override;
bool ShouldSuppressErrorPage(content::RenderFrame* render_frame,
const GURL& url,
@@ -41,6 +43,7 @@ class ContentRendererClientImpl : public content::ContentRendererClient {
void AddSupportedKeySystems(
std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems)
override;
+ void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override;
private:
#if defined(OS_ANDROID)
diff --git a/chromium/weblayer/renderer/error_page_helper.cc b/chromium/weblayer/renderer/error_page_helper.cc
index fa76ba35d1c..39a2ffb300b 100644
--- a/chromium/weblayer/renderer/error_page_helper.cc
+++ b/chromium/weblayer/renderer/error_page_helper.cc
@@ -44,9 +44,8 @@ bool IsReloadableError(const error_page::Error& error, bool was_failed_post) {
error.reason() != net::ERR_SSL_PROTOCOL_ERROR &&
// Do not trigger for blacklisted URLs.
// https://crbug.com/803839
- error.reason() != net::ERR_BLOCKED_BY_ADMINISTRATOR &&
// Do not trigger for requests that were blocked by the browser itself.
- error.reason() != net::ERR_BLOCKED_BY_CLIENT && !was_failed_post &&
+ !net::IsRequestBlockedError(error.reason()) && !was_failed_post &&
// Do not trigger for this error code because it is used by Chrome
// while an auth prompt is being displayed.
error.reason() != net::ERR_INVALID_AUTH_CREDENTIALS &&
@@ -133,11 +132,7 @@ void ErrorPageHelper::DidStartNavigation(
}
}
-void ErrorPageHelper::DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) {
- if (is_same_document_navigation)
- return;
-
+void ErrorPageHelper::DidCommitProvisionalLoad(ui::PageTransition transition) {
// If a page is committing, either it's an error page and autoreload will be
// started again below, or it's a success page and we need to clear autoreload
// state.
@@ -224,12 +219,17 @@ void ErrorPageHelper::SendCommand(
interface->ReportPhishingError();
break;
case security_interstitials::CMD_DO_REPORT:
+ // Used when user opts in to extended safe browsing
+ interface->DoReport();
+ break;
case security_interstitials::CMD_DONT_REPORT:
+ interface->DontReport();
+ break;
case security_interstitials::CMD_OPEN_REPORTING_PRIVACY:
+ interface->OpenReportingPrivacy();
+ break;
case security_interstitials::CMD_OPEN_WHITEPAPER:
- // Commands not used by the generic SSL error pages.
- // Also not currently used by the safebrowsing error pages.
- NOTREACHED();
+ interface->OpenWhitepaper();
break;
case security_interstitials::CMD_ERROR:
case security_interstitials::CMD_TEXT_FOUND:
diff --git a/chromium/weblayer/renderer/error_page_helper.h b/chromium/weblayer/renderer/error_page_helper.h
index 948ec3a7bfa..a134e113435 100644
--- a/chromium/weblayer/renderer/error_page_helper.h
+++ b/chromium/weblayer/renderer/error_page_helper.h
@@ -50,8 +50,7 @@ class ErrorPageHelper
void DidStartNavigation(
const GURL& url,
base::Optional<blink::WebNavigationType> navigation_type) override;
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidFinishLoad() override;
void OnStop() override;
void WasShown() override;
diff --git a/chromium/weblayer/renderer/weblayer_render_frame_observer.cc b/chromium/weblayer/renderer/weblayer_render_frame_observer.cc
index 6986b508257..ded0c16d562 100644
--- a/chromium/weblayer/renderer/weblayer_render_frame_observer.cc
+++ b/chromium/weblayer/renderer/weblayer_render_frame_observer.cc
@@ -38,7 +38,7 @@ WebLayerRenderFrameObserver::WebLayerRenderFrameObserver(
if (!render_frame->IsMainFrame())
return;
- // TODO(crbug.com/1025620): Handle case where subframe translation is enabled.
+ // TODO(crbug.com/1073370): Handle case where subframe translation is enabled.
DCHECK(!translate::IsSubFrameTranslationEnabled());
translate_agent_ =
new translate::TranslateAgent(render_frame, ISOLATED_WORLD_ID_TRANSLATE,
diff --git a/chromium/weblayer/shell/android/BUILD.gn b/chromium/weblayer/shell/android/BUILD.gn
index bc3a270505f..5a6d1f7ff78 100644
--- a/chromium/weblayer/shell/android/BUILD.gn
+++ b/chromium/weblayer/shell/android/BUILD.gn
@@ -203,7 +203,7 @@ android_apk("weblayer_support_apk") {
rebase_path("$root_gen_dir/CHROME_VERSION.json", root_build_dir)
native_lib_version_arg = "@FileArg($_native_lib_file:full-quoted)"
- shared_libraries = [ "//weblayer:libweblayer" ]
+ shared_libraries = [ "//weblayer:libweblayer_test" ]
}
python_library("weblayer_shell_wpt") {
diff --git a/chromium/weblayer/shell/android/browsertests_apk/metrics_browsertest.cc b/chromium/weblayer/shell/android/browsertests_apk/metrics_browsertest.cc
index 30ff1b25e2c..a4f0af9ae89 100644
--- a/chromium/weblayer/shell/android/browsertests_apk/metrics_browsertest.cc
+++ b/chromium/weblayer/shell/android/browsertests_apk/metrics_browsertest.cc
@@ -4,33 +4,51 @@
#include <deque>
-#include "base/android/jni_android.h"
#include "base/command_line.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/no_destructor.h"
#include "base/test/bind_test_util.h"
+#include "components/metrics/log_decoder.h"
+#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_switches.h"
+#include "content/public/test/browser_test_utils.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.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/profile.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"
-#include "weblayer/test/weblayer_browsertests_jni/MetricsTestHelper_jni.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
+namespace {
+
+bool HasHistogramWithHash(const metrics::ChromeUserMetricsExtension& uma_log,
+ uint64_t hash) {
+ for (int i = 0; i < uma_log.histogram_event_size(); ++i) {
+ if (uma_log.histogram_event(i).name_hash() == hash) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
class MetricsBrowserTest : public WebLayerBrowserTest {
public:
void SetUp() override {
- instance_ = this;
-
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(metrics::switches::kForceEnableMetricsReporting);
- Java_MetricsTestHelper_installTestGmsBridge(
- base::android::AttachCurrentThread(), HasUserConsent());
+ InstallTestGmsBridge(HasUserConsent(),
+ base::BindRepeating(&MetricsBrowserTest::OnLogMetrics,
+ base::Unretained(this)));
WebLayerMetricsServiceClient::GetInstance()->SetFastStartupForTesting(true);
WebLayerMetricsServiceClient::GetInstance()->SetUploadIntervalForTesting(
base::TimeDelta::FromMilliseconds(10));
@@ -38,20 +56,17 @@ class MetricsBrowserTest : public WebLayerBrowserTest {
}
void TearDown() override {
- Java_MetricsTestHelper_removeTestGmsBridge(
- base::android::AttachCurrentThread());
- instance_ = nullptr;
+ RemoveTestGmsBridge();
WebLayerBrowserTest::TearDown();
}
- static void OnLogMetrics(const metrics::ChromeUserMetricsExtension& metric) {
- if (!instance_)
- return;
- instance_->metrics_logs_.push_back(metric);
- std::move(instance_->on_new_log_).Run();
+ void OnLogMetrics(metrics::ChromeUserMetricsExtension metric) {
+ metrics_logs_.push_back(metric);
+ if (on_new_log_)
+ std::move(on_new_log_).Run();
}
- metrics::ChromeUserMetricsExtension waitForNextMetricsLog() {
+ metrics::ChromeUserMetricsExtension WaitForNextMetricsLog() {
if (metrics_logs_.empty()) {
base::RunLoop run_loop;
on_new_log_ = run_loop.QuitClosure();
@@ -71,23 +86,10 @@ class MetricsBrowserTest : public WebLayerBrowserTest {
std::unique_ptr<Profile> profile_;
std::deque<metrics::ChromeUserMetricsExtension> metrics_logs_;
base::OnceClosure on_new_log_;
- static MetricsBrowserTest* instance_;
};
-MetricsBrowserTest* MetricsBrowserTest::instance_ = nullptr;
-
-void JNI_MetricsTestHelper_OnLogMetrics(
- JNIEnv* env,
- const base::android::JavaParamRef<jbyteArray>& data) {
- metrics::ChromeUserMetricsExtension proto;
- jbyte* src_bytes = env->GetByteArrayElements(data, nullptr);
- proto.ParseFromArray(src_bytes, env->GetArrayLength(data.obj()));
- env->ReleaseByteArrayElements(data, src_bytes, JNI_ABORT);
- MetricsBrowserTest::OnLogMetrics(proto);
-}
-
IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, ProtoHasExpectedFields) {
- metrics::ChromeUserMetricsExtension log = waitForNextMetricsLog();
+ metrics::ChromeUserMetricsExtension log = WaitForNextMetricsLog();
EXPECT_EQ(metrics::ChromeUserMetricsExtension::ANDROID_WEBLAYER,
log.product());
EXPECT_TRUE(log.has_client_id());
@@ -118,7 +120,7 @@ IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, ProtoHasExpectedFields) {
}
IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, PageLoadsEnableMultipleUploads) {
- waitForNextMetricsLog();
+ WaitForNextMetricsLog();
// At this point, the MetricsService should be asleep, and should not have
// created any more metrics logs.
@@ -133,7 +135,7 @@ IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, PageLoadsEnableMultipleUploads) {
// This may take slightly longer than UPLOAD_INTERVAL_MS, due to the time
// spent processing the metrics log, but should be well within the timeout
// (unless something is broken).
- waitForNextMetricsLog();
+ WaitForNextMetricsLog();
// If we get here, we got a second metrics log (and the test may pass). If
// there was no second metrics log, then the above call will check fail with a
@@ -143,6 +145,35 @@ IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, PageLoadsEnableMultipleUploads) {
// might potentially have a third metrics log in the queue.
}
+IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, RendererHistograms) {
+ base::HistogramTester histogram_tester;
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ NavigateAndWaitForCompletion(
+ embedded_test_server()->GetURL("/simple_page.html"), shell());
+
+ uint64_t hash = base::HashMetricName("Android.SeccompStatus.RendererSandbox");
+
+ bool collect_final_metrics_for_log_called = false;
+
+ WebLayerMetricsServiceClient::GetInstance()
+ ->SetCollectFinalMetricsForLogClosureForTesting(
+ base::BindLambdaForTesting(
+ [&]() { collect_final_metrics_for_log_called = true; }));
+
+ // Not every WaitForNextMetricsLog call will end up calling
+ // MetricsServiceClient::CollectFinalMetricsForLog since there may already be
+ // staged logs to send (see ReportingService::SendNextLog). Since we need to
+ // wait for CollectFinalMetricsForLog to be run after the navigate call above,
+ // keep calling WaitForNextMetricsLog until CollectFinalMetricsForLog is
+ // called.
+ metrics::ChromeUserMetricsExtension uma_log;
+ while (!collect_final_metrics_for_log_called)
+ uma_log = WaitForNextMetricsLog();
+
+ ASSERT_TRUE(HasHistogramWithHash(uma_log, hash));
+}
+
class MetricsBrowserTestWithUserOptOut : public MetricsBrowserTest {
bool HasUserConsent() override { return false; }
};
diff --git a/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.cc b/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.cc
new file mode 100644
index 00000000000..5b78584f5a1
--- /dev/null
+++ b/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.cc
@@ -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.
+
+#include "weblayer/shell/android/browsertests_apk/metrics_test_helper.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/no_destructor.h"
+#include "weblayer/test/weblayer_browsertests_jni/MetricsTestHelper_jni.h"
+
+namespace weblayer {
+
+namespace {
+
+OnLogsMetricsCallback& GetOnLogMetricsCallback() {
+ static base::NoDestructor<OnLogsMetricsCallback> s_callback;
+ return *s_callback;
+}
+
+} // namespace
+
+void InstallTestGmsBridge(bool has_user_consent,
+ const OnLogsMetricsCallback on_log_metrics) {
+ GetOnLogMetricsCallback() = on_log_metrics;
+ Java_MetricsTestHelper_installTestGmsBridge(
+ base::android::AttachCurrentThread(), has_user_consent);
+}
+
+void RemoveTestGmsBridge() {
+ Java_MetricsTestHelper_removeTestGmsBridge(
+ base::android::AttachCurrentThread());
+ GetOnLogMetricsCallback().Reset();
+}
+
+void CreateProfile(const std::string& name) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_MetricsTestHelper_createProfile(
+ env, base::android::ConvertUTF8ToJavaString(env, name));
+}
+void DestroyProfile(const std::string& name) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_MetricsTestHelper_destroyProfile(
+ env, base::android::ConvertUTF8ToJavaString(env, name));
+}
+
+void JNI_MetricsTestHelper_OnLogMetrics(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jbyteArray>& data) {
+ auto& callback = GetOnLogMetricsCallback();
+ if (!callback)
+ return;
+
+ metrics::ChromeUserMetricsExtension proto;
+ jbyte* src_bytes = env->GetByteArrayElements(data, nullptr);
+ proto.ParseFromArray(src_bytes, env->GetArrayLength(data.obj()));
+ env->ReleaseByteArrayElements(data, src_bytes, JNI_ABORT);
+ callback.Run(proto);
+}
+
+} // namespace weblayer
diff --git a/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.h b/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.h
new file mode 100644
index 00000000000..6c54134cbcc
--- /dev/null
+++ b/chromium/weblayer/shell/android/browsertests_apk/metrics_test_helper.h
@@ -0,0 +1,36 @@
+// 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_SHELL_ANDROID_BROWSERTESTS_APK_METRICS_TEST_HELPER_H_
+#define WEBLAYER_SHELL_ANDROID_BROWSERTESTS_APK_METRICS_TEST_HELPER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
+
+namespace weblayer {
+
+// Various utilities to bridge to Java code for metrics related tests.
+
+using OnLogsMetricsCallback =
+ base::RepeatingCallback<void(metrics::ChromeUserMetricsExtension)>;
+
+// Call this in the SetUp() test harness method to install the test
+// GmsBridge and to set the metrics user consent state.
+void InstallTestGmsBridge(
+ bool has_user_consent,
+ const OnLogsMetricsCallback on_log_metrics = OnLogsMetricsCallback());
+
+// Call this in the TearDown() test harness method to remove the GmsBridge.
+void RemoveTestGmsBridge();
+
+// See Profile::Create()'s comments for the semantics of |name|.
+void CreateProfile(const std::string& name);
+
+void DestroyProfile(const std::string& name);
+
+} // namespace weblayer
+
+#endif // WEBLAYER_SHELL_ANDROID_BROWSERTESTS_APK_METRICS_TEST_HELPER_H_
diff --git a/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java b/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
index 4837ddac429..ecc26c1f6ca 100644
--- a/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
+++ b/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
@@ -14,11 +14,14 @@ import android.widget.RelativeLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
+import org.chromium.base.CommandLine;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.content_public.browser.BrowserStartupController;
import org.chromium.native_test.NativeBrowserTest;
import org.chromium.native_test.NativeBrowserTestActivity;
import org.chromium.weblayer.Browser;
+import org.chromium.weblayer.NewTabCallback;
+import org.chromium.weblayer.NewTabType;
import org.chromium.weblayer.Profile;
import org.chromium.weblayer.Tab;
import org.chromium.weblayer.TabCallback;
@@ -50,12 +53,13 @@ public class WebLayerBrowserTestsActivity extends NativeBrowserTestActivity {
WebLayer.loadAsync(getApplication(), webLayer -> {
mWebLayer = webLayer;
createShell();
+
+ NativeBrowserTest.javaStartupTasksComplete();
});
} catch (Exception e) {
throw new RuntimeException("failed loading WebLayer", e);
}
- NativeBrowserTest.javaStartupTasksComplete();
}
protected void createShell() {
@@ -76,7 +80,10 @@ public class WebLayerBrowserTestsActivity extends NativeBrowserTestActivity {
new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- Fragment fragment = WebLayer.createBrowserFragment("BrowserTestProfile");
+ CommandLine commandLine = CommandLine.getInstance();
+ String path = (commandLine.hasSwitch("start-in-incognito")) ? null : "BrowserTestProfile";
+
+ Fragment fragment = WebLayer.createBrowserFragment(path);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(viewId, fragment);
@@ -93,6 +100,14 @@ public class WebLayerBrowserTestsActivity extends NativeBrowserTestActivity {
mUrlView.setText(uri.toString());
}
});
+ // Set a new tab callback to make sure popups are added.
+ mTab.setNewTabCallback(new NewTabCallback() {
+ @Override
+ public void onNewTab(Tab tab, @NewTabType int type) {}
+
+ @Override
+ public void onCloseTab() {}
+ });
}
@Override
diff --git a/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java b/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java
index 41affaaf8ab..c89b0e38793 100644
--- a/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java
+++ b/chromium/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java
@@ -4,11 +4,16 @@
package org.chromium.weblayer_private;
+import android.content.Context;
+import android.text.TextUtils;
+
import org.chromium.base.Callback;
+import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer.WebLayer;
/**
* Helper for metrics_browsertest.cc
@@ -50,6 +55,26 @@ class MetricsTestHelper {
}
@CalledByNative
+ private static void createProfile(String name) {
+ Context appContext = ContextUtils.getApplicationContext();
+ WebLayer weblayer = WebLayer.loadSync(appContext);
+
+ String nameOrNull = null;
+ if (!TextUtils.isEmpty(name)) nameOrNull = name;
+ weblayer.getProfile(nameOrNull);
+ }
+
+ @CalledByNative
+ private static void destroyProfile(String name) {
+ Context appContext = ContextUtils.getApplicationContext();
+ WebLayer weblayer = WebLayer.loadSync(appContext);
+
+ String nameOrNull = null;
+ if (!TextUtils.isEmpty(name)) nameOrNull = name;
+ weblayer.getProfile(nameOrNull).destroy();
+ }
+
+ @CalledByNative
private static void removeTestGmsBridge() {
GmsBridge.injectInstance(null);
}
diff --git a/chromium/weblayer/shell/android/shell_apk/AndroidManifest.xml b/chromium/weblayer/shell/android/shell_apk/AndroidManifest.xml
index fabe6849287..83ab34ddbd4 100644
--- a/chromium/weblayer/shell/android/shell_apk/AndroidManifest.xml
+++ b/chromium/weblayer/shell/android/shell_apk/AndroidManifest.xml
@@ -9,7 +9,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.weblayer.shell">
- <application android:label="WebLayer shell">
+ <application android:label="WebLayer shell"
+ android:supportsRtl="true">
<activity android:name="WebLayerShellActivity"
android:launchMode="singleTask"
android:theme="@style/ShellTheme"
diff --git a/chromium/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml b/chromium/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
index a07648248cc..f0f01f50784 100644
--- a/chromium/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
+++ b/chromium/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
@@ -28,6 +28,7 @@
android:imeOptions="actionGo"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:importantForAutofill="no"
android:textSize="15sp"/>
<View
diff --git a/chromium/weblayer/shell/android/shell_apk/res/menu/app_menu.xml b/chromium/weblayer/shell/android/shell_apk/res/menu/app_menu.xml
index 02821be9b0f..2d6ff129de0 100644
--- a/chromium/weblayer/shell/android/shell_apk/res/menu/app_menu.xml
+++ b/chromium/weblayer/shell/android/shell_apk/res/menu/app_menu.xml
@@ -17,4 +17,6 @@
android:title="Bottom view" />
<item android:id="@+id/site_settings_menu_id"
android:title="Site Settings" />
+ <item android:id="@+id/translate_menu_id"
+ android:title="Translate" />
</menu>
diff --git a/chromium/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/chromium/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index bb67a110ab9..fa140357af6 100644
--- a/chromium/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/chromium/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -4,6 +4,7 @@
package org.chromium.weblayer.shell;
+import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -19,6 +20,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import android.webkit.URLUtil;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
@@ -32,6 +34,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import org.chromium.base.CommandLine;
import org.chromium.base.IntentUtils;
import org.chromium.weblayer.Browser;
import org.chromium.weblayer.ContextMenuParams;
@@ -43,6 +46,7 @@ import org.chromium.weblayer.NavigationController;
import org.chromium.weblayer.NewTabCallback;
import org.chromium.weblayer.NewTabType;
import org.chromium.weblayer.Profile;
+import org.chromium.weblayer.SettingType;
import org.chromium.weblayer.SiteSettingsActivity;
import org.chromium.weblayer.Tab;
import org.chromium.weblayer.TabCallback;
@@ -58,7 +62,7 @@ import java.util.List;
* Activity for managing the Demo Shell.
*/
public class WebLayerShellActivity extends FragmentActivity {
- private static final String PROFILE_NAME = "DefaultProfile";
+ private static final String NON_INCOGNITO_PROFILE_NAME = "DefaultProfile";
private static class ContextMenuCreator
implements View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
@@ -133,6 +137,7 @@ public class WebLayerShellActivity extends FragmentActivity {
private List<Tab> mPreviousTabList = new ArrayList<>();
private Runnable mExitFullscreenRunnable;
private View mBottomView;
+ private boolean mInIncognitoMode;
@Override
protected void onCreate(final Bundle savedInstanceState) {
@@ -173,6 +178,9 @@ public class WebLayerShellActivity extends FragmentActivity {
popup.getMenuInflater().inflate(R.menu.app_menu, popup.getMenu());
MenuItem bottomMenuItem = popup.getMenu().findItem(R.id.toggle_bottom_view_id);
bottomMenuItem.setChecked(mBottomView != null);
+ popup.getMenu()
+ .findItem(R.id.translate_menu_id)
+ .setVisible(mBrowser.getActiveTab().canTranslate());
popup.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.reload_menu_id) {
mBrowser.getActiveTab().getNavigationController().reload();
@@ -205,12 +213,20 @@ public class WebLayerShellActivity extends FragmentActivity {
}
if (item.getItemId() == R.id.site_settings_menu_id) {
- Intent intent =
- SiteSettingsActivity.createIntentForCategoryList(this, PROFILE_NAME);
+ // TODO(crbug.com/1083233): Figure out the right long-term behavior here.
+ if (mInIncognitoMode) return true;
+
+ Intent intent = SiteSettingsActivity.createIntentForCategoryList(
+ this, NON_INCOGNITO_PROFILE_NAME);
IntentUtils.safeStartActivity(this, intent);
return true;
}
+ if (item.getItemId() == R.id.translate_menu_id) {
+ mBrowser.getActiveTab().showTranslateUi();
+ return true;
+ }
+
return false;
});
popup.show();
@@ -254,6 +270,7 @@ public class WebLayerShellActivity extends FragmentActivity {
fragment.setRetainInstance(true);
mBrowser = Browser.fromFragment(fragment);
mProfile = mBrowser.getProfile();
+ mProfile.setBooleanSetting(SettingType.UKM_ENABLED, true);
setTabCallbacks(mBrowser.getActiveTab(), fragment);
mBrowser.setTopView(mTopContentsContainer);
@@ -287,7 +304,7 @@ public class WebLayerShellActivity extends FragmentActivity {
return;
}
String startupUrl = getUrlFromIntent(getIntent());
- if (TextUtils.isEmpty(startupUrl)) {
+ if (TextUtils.isEmpty(startupUrl) || !URLUtil.isValidUrl(startupUrl)) {
startupUrl = "https://google.com";
}
loadUrl(startupUrl);
@@ -380,10 +397,10 @@ public class WebLayerShellActivity extends FragmentActivity {
public void bringTabToFront() {
tab.getBrowser().setActiveTab(tab);
- Context context = WebLayerShellActivity.this;
- Intent intent = new Intent(context, WebLayerShellActivity.class);
+ Activity activity = WebLayerShellActivity.this;
+ Intent intent = new Intent(activity, WebLayerShellActivity.class);
intent.setAction(Intent.ACTION_MAIN);
- context.getApplicationContext().startActivity(intent);
+ activity.startActivity(intent);
}
});
tab.getNavigationController().registerNavigationCallback(new NavigationCallback() {
@@ -427,7 +444,14 @@ public class WebLayerShellActivity extends FragmentActivity {
}
}
- Fragment fragment = WebLayer.createBrowserFragment(PROFILE_NAME);
+ if (CommandLine.isInitialized()
+ && CommandLine.getInstance().hasSwitch("start-in-incognito")) {
+ mInIncognitoMode = true;
+ }
+
+ String profileName = mInIncognitoMode ? null : NON_INCOGNITO_PROFILE_NAME;
+
+ Fragment fragment = WebLayer.createBrowserFragment(profileName);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(mMainViewId, fragment);
diff --git a/chromium/weblayer/shell/app/shell_main_params.cc b/chromium/weblayer/shell/app/shell_main_params.cc
index 36e58d734ae..9ece1e23cda 100644
--- a/chromium/weblayer/shell/app/shell_main_params.cc
+++ b/chromium/weblayer/shell/app/shell_main_params.cc
@@ -49,29 +49,45 @@ GURL GetStartupURL() {
class MainDelegateImpl : public MainDelegate {
public:
void PreMainMessageLoopRun() override {
+ // On Android the Profile is created and owned in Java via an
+ // embedder-specific call to WebLayer.createBrowserFragment().
+#if !defined(OS_ANDROID)
InitializeProfile();
+#endif
Shell::Initialize();
+#if defined(OS_ANDROID)
+ Shell::CreateNewWindow(GetStartupURL(), gfx::Size());
+#else
Shell::CreateNewWindow(profile_.get(), GetStartupURL(), gfx::Size());
+#endif
}
- void PostMainMessageLoopRun() override { DestroyProfile(); }
+ void PostMainMessageLoopRun() override {
+#if !defined(OS_ANDROID)
+ DestroyProfile();
+#endif
+ }
void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) override {
Shell::SetMainMessageLoopQuitClosure(std::move(quit_closure));
}
private:
+#if !defined(OS_ANDROID)
void InitializeProfile() {
- profile_ = Profile::Create("web_shell");
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ std::string profile_name =
+ command_line->HasSwitch(switches::kStartInIncognito) ? "" : "web_shell";
- // TODO: create an incognito profile as well.
+ profile_ = Profile::Create(profile_name);
}
void DestroyProfile() { profile_.reset(); }
std::unique_ptr<Profile> profile_;
+#endif
};
} // namespace
diff --git a/chromium/weblayer/shell/browser/shell.cc b/chromium/weblayer/shell/browser/shell.cc
index 5f3c69dad3f..58adffab13d 100644
--- a/chromium/weblayer/shell/browser/shell.cc
+++ b/chromium/weblayer/shell/browser/shell.cc
@@ -16,6 +16,8 @@
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "url/gurl.h"
+#include "weblayer/browser/browser_impl.h"
+#include "weblayer/browser/browser_list.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/navigation_controller.h"
@@ -30,26 +32,26 @@ const int kDefaultTestWindowHeightDip = 700;
std::vector<Shell*> Shell::windows_;
-Shell::Shell(std::unique_ptr<Tab> tab)
- : tab_(std::move(tab)), window_(nullptr) {
+Shell::Shell(std::unique_ptr<Browser> browser)
+ : browser_(std::move(browser)), window_(nullptr) {
windows_.push_back(this);
- if (tab_) {
- tab_->AddObserver(this);
- tab_->GetNavigationController()->AddObserver(this);
+ if (tab()) {
+ tab()->AddObserver(this);
+ tab()->GetNavigationController()->AddObserver(this);
#if !defined(OS_ANDROID) // Android does this in Java.
// TODO: how will tests work with this on android? can we get to the
// concrete type?
- static_cast<TabImpl*>(tab_.get())->profile()->SetDownloadDelegate(this);
+ static_cast<TabImpl*>(tab())->profile()->SetDownloadDelegate(this);
#endif
}
}
Shell::~Shell() {
- if (tab_) {
- tab_->GetNavigationController()->RemoveObserver(this);
- tab_->RemoveObserver(this);
+ if (tab()) {
+ tab()->GetNavigationController()->RemoveObserver(this);
+ tab()->RemoveObserver(this);
}
PlatformCleanUp();
@@ -63,17 +65,19 @@ Shell::~Shell() {
// Always destroy WebContents before calling PlatformExit(). WebContents
// destruction sequence may depend on the resources destroyed in
// PlatformExit() (e.g. the display::Screen singleton).
- tab_.reset();
+ browser_.reset();
if (windows_.empty()) {
+ PlatformExit();
+
if (*g_quit_main_message_loop)
std::move(*g_quit_main_message_loop).Run();
}
}
-Shell* Shell::CreateShell(std::unique_ptr<Tab> tab,
+Shell* Shell::CreateShell(std::unique_ptr<Browser> browser,
const gfx::Size& initial_size) {
- Shell* shell = new Shell(std::move(tab));
+ Shell* shell = new Shell(std::move(browser));
shell->PlatformCreateWindow(initial_size.width(), initial_size.height());
shell->PlatformSetContents();
@@ -90,13 +94,6 @@ void Shell::CloseAllWindows() {
// Pump the message loop to allow window teardown tasks to run.
base::RunLoop().RunUntilIdle();
-
- // If there were no windows open then the message loop quit closure will
- // not have been run.
- if (*g_quit_main_message_loop)
- std::move(*g_quit_main_message_loop).Run();
-
- PlatformExit();
}
void Shell::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) {
@@ -104,11 +101,21 @@ void Shell::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) {
}
Tab* Shell::tab() {
+ if (!browser())
+ return nullptr;
+ CHECK(!browser()->GetTabs().empty());
+ return browser()->GetTabs()[0];
+}
+
+Browser* Shell::browser() {
#if defined(OS_ANDROID)
// TODO(jam): this won't work if we need more than one Shell in a test.
- return Tab::GetLastTabForTesting();
+ const auto& browsers = BrowserList::GetInstance()->browsers();
+ if (browsers.empty())
+ return nullptr;
+ return *(browsers.begin());
#else
- return tab_.get();
+ return browser_.get();
#endif
}
@@ -121,7 +128,8 @@ void Shell::DisplayedUrlChanged(const GURL& url) {
}
void Shell::LoadStateChanged(bool is_loading, bool to_different_document) {
- NavigationController* navigation_controller = tab_->GetNavigationController();
+ NavigationController* navigation_controller =
+ tab()->GetNavigationController();
PlatformEnableUIControl(STOP_BUTTON, is_loading && to_different_document);
@@ -158,16 +166,26 @@ gfx::Size Shell::AdjustWindowSize(const gfx::Size& initial_size) {
return GetShellDefaultSize();
}
+#if defined(OS_ANDROID)
+Shell* Shell::CreateNewWindow(const GURL& url, const gfx::Size& initial_size) {
+ // On Android, the browser is owned by the Java side.
+ return CreateNewWindowWithBrowser(nullptr, url, initial_size);
+}
+#else
Shell* Shell::CreateNewWindow(Profile* web_profile,
const GURL& url,
const gfx::Size& initial_size) {
-#if defined(OS_ANDROID)
- std::unique_ptr<Tab> tab;
-#else
- auto tab = Tab::Create(web_profile);
+ auto browser = Browser::Create(web_profile, nullptr);
+ browser->CreateTab();
+ return CreateNewWindowWithBrowser(std::move(browser), url, initial_size);
+}
#endif
- Shell* shell = CreateShell(std::move(tab), AdjustWindowSize(initial_size));
+Shell* Shell::CreateNewWindowWithBrowser(std::unique_ptr<Browser> browser,
+ const GURL& url,
+ const gfx::Size& initial_size) {
+ Shell* shell =
+ CreateShell(std::move(browser), AdjustWindowSize(initial_size));
if (!url.is_empty())
shell->LoadURL(url);
return shell;
diff --git a/chromium/weblayer/shell/browser/shell.h b/chromium/weblayer/shell/browser/shell.h
index 78f48accb9f..5664d767ba3 100644
--- a/chromium/weblayer/shell/browser/shell.h
+++ b/chromium/weblayer/shell/browser/shell.h
@@ -35,6 +35,7 @@ class WMState;
class GURL;
namespace weblayer {
+class Browser;
class Profile;
class Tab;
@@ -56,9 +57,13 @@ class Shell : public TabObserver,
// Do one time initialization at application startup.
static void Initialize();
+#if defined(OS_ANDROID)
+ static Shell* CreateNewWindow(const GURL& url, const gfx::Size& initial_size);
+#else
static Shell* CreateNewWindow(Profile* web_profile,
const GURL& url,
const gfx::Size& initial_size);
+#endif
// Returns the currently open windows.
static std::vector<Shell*>& windows() { return windows_; }
@@ -72,6 +77,7 @@ class Shell : public TabObserver,
static void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure);
Tab* tab();
+ Browser* browser();
gfx::NativeWindow window() { return window_; }
@@ -80,7 +86,11 @@ class Shell : public TabObserver,
private:
enum UIControl { BACK_BUTTON, FORWARD_BUTTON, STOP_BUTTON };
- explicit Shell(std::unique_ptr<Tab> tab);
+ static Shell* CreateNewWindowWithBrowser(std::unique_ptr<Browser> browser,
+ const GURL& url,
+ const gfx::Size& initial_size);
+
+ explicit Shell(std::unique_ptr<Browser> browser);
// TabObserver implementation:
void DisplayedUrlChanged(const GURL& url) override;
@@ -102,7 +112,7 @@ class Shell : public TabObserver,
AllowDownloadCallback callback) override;
// Helper to create a new Shell.
- static Shell* CreateShell(std::unique_ptr<Tab> tab,
+ static Shell* CreateShell(std::unique_ptr<Browser> browser,
const gfx::Size& initial_size);
// Helper for one time initialization of application
@@ -141,7 +151,7 @@ class Shell : public TabObserver,
// Set the title of shell window
void PlatformSetTitle(const base::string16& title);
- std::unique_ptr<Tab> tab_;
+ std::unique_ptr<Browser> browser_;
gfx::NativeWindow window_;
diff --git a/chromium/weblayer/shell/browser/shell_views.cc b/chromium/weblayer/shell/browser/shell_views.cc
index cc442c555b1..ed799835e8f 100644
--- a/chromium/weblayer/shell/browser/shell_views.cc
+++ b/chromium/weblayer/shell/browser/shell_views.cc
@@ -6,6 +6,8 @@
#include <stddef.h>
+#include <memory>
+
#include "base/command_line.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -52,7 +54,10 @@ class ShellWindowDelegateView : public views::WidgetDelegateView,
public:
enum UIControl { BACK_BUTTON, FORWARD_BUTTON, STOP_BUTTON };
- ShellWindowDelegateView(Shell* shell) : shell_(shell) {}
+ explicit ShellWindowDelegateView(Shell* shell) : shell_(shell) {
+ SetHasWindowSizeControls(true);
+ InitShellWindow();
+ }
~ShellWindowDelegateView() override {}
@@ -192,11 +197,11 @@ class ShellWindowDelegateView : public views::WidgetDelegateView,
}
layout->AddPaddingRow(0, 5);
-
- InitAccelerators();
}
void InitAccelerators() {
+ // This function must be called when part of the widget hierarchy.
+ DCHECK(GetWidget());
static const ui::KeyboardCode keys[] = {ui::VKEY_F5, ui::VKEY_BROWSER_BACK,
ui::VKEY_BROWSER_FORWARD};
for (size_t i = 0; i < base::size(keys); ++i) {
@@ -239,16 +244,7 @@ class ShellWindowDelegateView : public views::WidgetDelegateView,
}
// Overridden from WidgetDelegateView
- bool CanResize() const override { return true; }
- bool CanMaximize() const override { return true; }
- bool CanMinimize() const override { return true; }
base::string16 GetWindowTitle() const override { return title_; }
- void WindowClosing() override {
- if (shell_) {
- delete shell_;
- shell_ = nullptr;
- }
- }
// Overridden from View
gfx::Size GetMinimumSize() const override {
@@ -256,12 +252,7 @@ class ShellWindowDelegateView : public views::WidgetDelegateView,
// (preferred) size.
return gfx::Size();
}
- void ViewHierarchyChanged(
- const views::ViewHierarchyChangedDetails& details) override {
- if (details.is_add && details.child == this) {
- InitShellWindow();
- }
- }
+ void AddedToWidget() override { InitAccelerators(); }
// Overridden from AcceleratorTarget:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
@@ -281,8 +272,7 @@ class ShellWindowDelegateView : public views::WidgetDelegateView,
}
private:
- // Hold a reference of Shell for deleting it when the window is closing
- Shell* shell_;
+ std::unique_ptr<Shell> shell_;
// Window title
base::string16 title_;
@@ -382,7 +372,7 @@ void Shell::PlatformSetContents() {
views::WidgetDelegate* widget_delegate = window_widget_->widget_delegate();
ShellWindowDelegateView* delegate_view =
static_cast<ShellWindowDelegateView*>(widget_delegate);
- delegate_view->AttachTab(tab_.get(), content_size_);
+ delegate_view->AttachTab(tab(), content_size_);
window_->GetHost()->Show();
window_widget_->Show();
}
diff --git a/chromium/weblayer/shell/common/shell_switches.cc b/chromium/weblayer/shell/common/shell_switches.cc
index e70106da503..fa058d07acf 100644
--- a/chromium/weblayer/shell/common/shell_switches.cc
+++ b/chromium/weblayer/shell/common/shell_switches.cc
@@ -10,5 +10,8 @@ namespace switches {
// Stops new Shell objects from navigating to a default url.
const char kNoInitialNavigation[] = "no-initial-navigation";
+// Starts the shell with the profile in incognito mode.
+const char kStartInIncognito[] = "start-in-incognito";
+
} // namespace switches
} // namespace weblayer
diff --git a/chromium/weblayer/shell/common/shell_switches.h b/chromium/weblayer/shell/common/shell_switches.h
index 3219f68433b..dfbb5b0e7df 100644
--- a/chromium/weblayer/shell/common/shell_switches.h
+++ b/chromium/weblayer/shell/common/shell_switches.h
@@ -10,6 +10,8 @@ namespace switches {
extern const char kNoInitialNavigation[];
+extern const char kStartInIncognito[];
+
} // namespace switches
} // namespace weblayer
diff --git a/chromium/weblayer/test/BUILD.gn b/chromium/weblayer/test/BUILD.gn
index b7035240fff..b32d79916e2 100644
--- a/chromium/weblayer/test/BUILD.gn
+++ b/chromium/weblayer/test/BUILD.gn
@@ -88,14 +88,20 @@ test("weblayer_browsertests") {
"//components/autofill/core/browser",
"//components/autofill/core/browser:test_support",
"//components/autofill/core/common",
+ "//components/blocked_content",
"//components/content_settings/core/browser",
+ "//components/network_session_configurator/common",
"//components/network_time",
"//components/page_load_metrics/browser:browser",
+ "//components/prefs",
"//components/security_interstitials/content:security_interstitial_page",
"//components/sessions:test_support",
+ "//components/site_isolation",
"//components/strings",
"//components/translate/content/browser",
"//components/translate/content/browser:test_support",
+ "//components/ukm:ukm_test_helper",
+ "//components/user_prefs",
"//components/variations",
"//components/variations/net",
"//content/public/browser",
@@ -113,10 +119,16 @@ test("weblayer_browsertests") {
"../browser/cookie_manager_browsertest.cc",
"../browser/download_browsertest.cc",
"../browser/errorpage_browsertest.cc",
+ "../browser/js_communication/web_message_browsertest.cc",
"../browser/navigation_browsertest.cc",
"../browser/page_load_metrics_browsertest.cc",
+ "../browser/popup_blocker_browsertest.cc",
+ "../browser/prefetch_browsertest.cc",
+ "../browser/profile_browsertest.cc",
+ "../browser/site_isolation_browsertest.cc",
"../browser/ssl_browsertest.cc",
"../browser/translate_browsertest.cc",
+ "../browser/url_bar/url_bar_browsertest.cc",
"../browser/weblayer_variations_http_browsertest.cc",
"../browser/webui/webui_browsertest.cc",
"browsertests_main.cc",
@@ -140,8 +152,11 @@ test("weblayer_browsertests") {
if (is_android) {
sources += [
"../browser/safe_browsing/safe_browsing_browsertest.cc",
+ "../browser/ukm_browsertest.cc",
"../browser/url_bar/page_info_browsertest.cc",
"../shell/android/browsertests_apk/metrics_browsertest.cc",
+ "../shell/android/browsertests_apk/metrics_test_helper.cc",
+ "../shell/android/browsertests_apk/metrics_test_helper.h",
"../shell/android/browsertests_apk/weblayer_browser_tests_jni_onload.cc",
]
deps += [
@@ -151,6 +166,7 @@ test("weblayer_browsertests") {
"//android_webview:generate_aw_strings_grit",
"//android_webview:locale_pak_assets",
"//android_webview:pak_file_assets",
+ "//components/infobars/core",
"//components/metrics",
"//components/page_info",
"//components/page_info/android",
@@ -167,7 +183,6 @@ test("weblayer_browsertests") {
"//ui/android:android",
"//ui/touch_selection:touch_selection",
"//weblayer:locale_pak_assets",
- "//weblayer/browser/safe_browsing:safe_browsing",
# Needed for WebLayerImpl.
"//weblayer/browser/java",
@@ -180,7 +195,7 @@ test("weblayer_browsertests") {
"${target_gen_dir}/weblayer_browsertests_manifest/AndroidManifest.xml"
android_manifest_dep = ":weblayer_browsertests_manifest"
use_default_launcher = false
- shared_resources = true
+ app_as_shared_lib = true
product_config_java_packages = [ weblayer_product_config_java_package ]
} else {
sources += [
@@ -189,7 +204,6 @@ test("weblayer_browsertests") {
# integration tests.
"../browser/persistence/browser_persister_browsertest.cc",
"../browser/persistence/minimal_browser_persister_browsertest.cc",
- "../browser/url_bar/url_bar_browsertest.cc",
]
}
}
@@ -200,13 +214,20 @@ source_set("run_all_unittests") {
public_deps = [
"//base/test:test_support",
"//content/test:test_support",
+ "//weblayer:weblayer_lib_base",
]
}
test("weblayer_unittests") {
deps = [
":run_all_unittests",
- "//weblayer:weblayer_lib_base",
+ "//components/site_isolation",
+ ]
+ if (is_android) {
+ deps += [ ":weblayer_test_assets" ]
+ }
+ sources = [
+ "../browser/profile_disk_operations_unittests.cc",
+ "../browser/site_isolation_policy_unittest.cc",
]
- sources = [ "../browser/profile_disk_operations_unittests.cc" ]
}
diff --git a/chromium/weblayer/tools/run_weblayer_shell.py b/chromium/weblayer/tools/run_weblayer_shell.py
index a125f531407..de41798b1ea 100755
--- a/chromium/weblayer/tools/run_weblayer_shell.py
+++ b/chromium/weblayer/tools/run_weblayer_shell.py
@@ -27,7 +27,9 @@ def main():
default=[],
help='Target device for apk to install on. Enter multiple'
' times for multiple devices.')
- parser.add_argument('remaining_args', nargs=argparse.REMAINDER)
+ parser.add_argument('remaining_args', nargs=argparse.REMAINDER,
+ help='Flags to be passed to WebLayer should be appended'
+ ' as --args="--myflag"')
args = parser.parse_args()
devil_chromium.Initialize()
diff --git a/chromium/weblayer/weblayer_resource_exclusions.gni b/chromium/weblayer/weblayer_resource_exclusions.gni
new file mode 100644
index 00000000000..169673ed166
--- /dev/null
+++ b/chromium/weblayer/weblayer_resource_exclusions.gni
@@ -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.
+
+_material_package = "*com_google_android_material_material*"
+
+weblayer_resource_exclusion_exceptions = [
+ # TextInputLayout (used for HTTP Auth dialog)
+ "${_material_package}/design_text_*",
+ "${_material_package}/text_*",
+
+ # Used for the translate infobar
+ "${_material_package}/design_layout_tab_*",
+]
+
+# Copied from chrome_public_apk_tmpl.gni.
+
+# Remove unneeded entries from material design values.xml files.
+weblayer_resource_values_filter_rules = [
+ "${_material_package}:[Bb]adge",
+ "${_material_package}:[Bb]ottomNavigation",
+ "${_material_package}:[Bb]ottomSheet",
+ "${_material_package}:[Bb]uttonToggleGroup",
+ "${_material_package}:[Cc]alendar",
+ "${_material_package}:[Cc]ardView",
+ "${_material_package}:\b[Cc]hip",
+ "${_material_package}:design_snackbar",
+ "${_material_package}:[Ff]loatingActionButton",
+ "${_material_package}:[Mm]aterialAlertDialog",
+ "${_material_package}:mtrl_alert",
+ "${_material_package}:mtrl_navigation",
+ "${_material_package}:mtrl_slider",
+ "${_material_package}:[Nn]avigationView",
+ "${_material_package}:picker",
+ "${_material_package}:[Ss]nackbar",
+ "${_material_package}:[Ss]lider",
+ "${_material_package}:[Tt]oolbarLayout",
+]
+
+_material_package = "com_google_android_material_material.*"
+
+# Used only by alert dialog on tiny screens.
+weblayer_resource_exclusion_regex = "${_material_package}values-small"
+
+# Used only by date picker (which chrome doesn't use).
+weblayer_resource_exclusion_regex +=
+ "|${_material_package}-(w480dp-port|w360dp-port|h480dp-land|h360dp-land)"
+
+# Material design layouts that cause views to be kept that we don't use.
+# Instead of manually filtering, unused resource removal would be better:
+# https://crbug.com/636448
+weblayer_resource_exclusion_regex += "|${_material_package}/layout"
+weblayer_resource_exclusion_regex += "|${_material_package}/color.*(choice|chip_|card_|calendar_|bottom_nav_|slider_)"
+weblayer_resource_exclusion_regex +=
+ "|${_material_package}/drawable.*design_snackbar"
+weblayer_resource_exclusion_regex += "|${_material_package}/xml.*badge_"